Setup

# library(ggplot2)
# library(reshape2)
library(ggpattern)
library(viridis)
library(colorRamps)
library(gridExtra)
library(ggplot2)
library('igraph')
library(ggnet)
library(network)
library(khroma)
library(dplyr)

source('similarity.R')

sunset <- colour("sunset")
discrete_rainbow <- colour("discrete rainbow")

path.base = '../../../'
path.work = paste(path.base, '02_analysis/04_sv/01_data/', sep = '')
path.tair = paste(path.base, '01_data_common/01_tair10/', sep = '')
path.figures = paste(path.base, '02_analysis/04_sv/03_figures/', sep = '')
path.svs = paste(path.base, '01_data_common/02_annot_denovo/02_pannagram/svs/', sep = '')
# path.genes = paste(path.base, '01_data_common/02_annot_denovo/02_pannagram/genes/', sep = '')

# sim.cutoff = 0.9

sim.cutoff = 0.85

Coolors



fam.palette = c()
fam.palette['Unassigned'] = 'grey'
fam.palette['Mix'] = 'grey20'
fam.palette['Mix with Helitron'] = '#266D98'
fam.palette['Helitron'] = '#BCACDE'
fam.palette["LTR/Copia"] = '#BFDB38'
fam.palette["LTR/Gypsy"] = '#54B435'
fam.palette["DNA/HAT"] = '#F9B5D0'
fam.palette["DNA+"] = '#C8658C'
fam.palette["DNA/MuDR"] = '#971549'


fam.palette["LINE"] = '#FFC26F'
fam.palette["RathE1/2/3_cons"] = '#C38154'
fam.palette["SINE"] = '#884A39'
fam.palette["TEG"] = '#4E3636'

TEs


# Load similarity function

bl.file = paste(path.work,'new_te_on_te.fasta',sep = '')
bl.res = read.table(bl.file)
bl.res = bl.res[bl.res$V1 != bl.res$V8,]

bl.res.init = bl.res
bl.res = bl.res[bl.res$V6 >= sim.cutoff * 100,]

res.nest = findNestedness(bl.res, use.strand = F)
[1] 130447
[1] 17626
[1] 3789
[1] 1186
[1] 407
[1] 180
[1] 79
[1] 54
[1] 34
[1] 26
[1] 17
[1] 10
[1] 3
[1] 1
[1] 0
[1] 124919
[1] 17576
[1] 3842
[1] 1240
[1] 437
[1] 189
[1] 89
[1] 61
[1] 41
[1] 28
[1] 21
[1] 11
[1] 6
[1] 2
[1] 0
res.nest.len = sapply(unique(c(res.nest$V1, res.nest$V8)), function(s) as.numeric(strsplit(s, '\\|')[[1]][5]))
  
res.nest$len1 = res.nest.len[res.nest$V1]
res.nest$len8 = res.nest.len[res.nest$V8]
res.nest$p1 = res.nest$C1 / res.nest$len1
res.nest$p8 = res.nest$C8 / res.nest$len8

res.nest.sim = res.nest[(res.nest$p1 >= sim.cutoff) | 
                          (res.nest$p8 >= sim.cutoff),]

How many TEs are in the graph

Distribution among families and subfamilies Distribution among lengths

te.in.graph = unique(c(res.nest$V1, res.nest$V8))

# What is the actual number of TEs
file.content <- readLines(bl.file)

selected.lines <- file.content[grepl("^# Query:|hits found", file.content)]
df.query = data.frame(b.query=selected.lines[seq(1, length(selected.lines), by = 2)],
                      b.hits=selected.lines[seq(2, length(selected.lines), by = 2)])

df.query$query  <- gsub("^# Query: (.*)", "\\1", df.query$b.query)
df.query$len <- as.numeric(sapply(strsplit(df.query$query, "\\|"), function(x) x[5]))
df.query$hits <- as.numeric(stringr::str_extract(df.query$b.hits, "\\d+"))
df.query$val.hits = df.query$hits
df.query$val.hits[df.query$val.hits >= 2] = 2
df.query$val.hits[df.query$query %in% bl.res$V8] = 2
df.query$val.hits[df.query$query %in% te.in.graph] = 3
hit.values = c('0 hits', '1 self-hit', 'partial overlap', 'in graph', "in graph but not in SVs")
df.query$s.hits = hit.values[df.query$val.hits+1]
df.query$s.hits = factor(df.query$s.hits, levels = rev(hit.values))
df.query$family <- sapply(strsplit(df.query$query, "\\|"), function(x) x[9])
df.query$subfam <- sapply(strsplit(df.query$query, "\\|"), function(x) x[8])


my_colors <- colors <- c("in graph" = "#676FA3",
            "partial overlap" = "#FF9F29",
            "1 self-hit" = "#6EBF8B",
            "0 hits" = "#D82148",
            "in graph but not in SVs" = "#151D3B")


# TEs, which are not in SVs
te.in.svs = read.table(paste(path.work, 'blast_tes_on_sv.txt', sep = ''), stringsAsFactors = F)
te.rest = setdiff(df.query$query, te.in.svs$V1)
te.in.svs = read.table(paste(path.work, 'blast_sv_on_tes.txt', sep = ''), stringsAsFactors = F)
te.rest = setdiff(te.rest, te.in.svs$V8)
df.query$s.hits[df.query$query %in% te.rest] = "in graph but not in SVs"


p = ggplot(df.query, aes(x = len, fill = s.hits, color = s.hits)) +
  # geom_histogram(aes(y = ..density..), alpha=0.5, color = "black", bins = 30) +
  # geom_jitter(height = 0.02, width = 0, alpha = 0.7) +
  geom_density(alpha = 0.5) +
  scale_fill_manual(values = my_colors) +
  scale_color_manual(values = my_colors) +
   scale_x_log10() +
  labs(fill = NULL, color = NULL) +
  xlab('length of TEs') + ylab('Normalised density') +
  theme_minimal() +
  theme(legend.position = c(1, 1), legend.justification = c(1, 1),
          legend.background = element_rect(color = "grey90"))

p

pdf(paste(path.figures, 'tes_self_blast_len_density.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

table(df.query$val.hits)

    0     1     2     3 
 1584 13615   766 19125 

TEs not in SVs

# TEs in te-graph: te.in.graph
# TEs which are have no connection to SVs

df = as.data.frame(table(df.query$s.hits))

  
colors <- c("in graph" = "#676FA3",
            "partial overlap" = "#FF9F29",
            "1 self-hit" = "#6EBF8B",
            "0 hits" = "#D82148",
            "in graph but not in SVs" = "#151D3B")


p = ggplot(df, aes(x = "", y = Freq, fill = Var1)) +
  geom_bar(stat="identity", width=1, alpha = 0.7) +
  coord_polar("y", start=0) +
  labs(title=NULL, fill="Categories") +
  theme_void()+
    scale_fill_manual(values = colors) +
  geom_text(aes(label = Freq,x = 1.3), position = position_stack(vjust = 0.5)) + theme(legend.position="none")
p


pdf(paste(path.figures, 'tes_self_blast_pie_chart.pdf', sep = ''), width = 3, height = 3)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Examples

Examples no hits


pdf(paste(path.figures, 'tes_self_scatter_no_hits_long.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 

head(df.query.tmp[df.query.tmp$family == 'DNA/MuDR',]$query)
[1] "te|641277|641420|1|144|+|AT1TE02080|ARNOLDY1|DNA/MuDR"     
[2] "te|1157763|1157863|1|101|+|AT1TE03780|LIMPET1|DNA/MuDR"    
[3] "te|4546279|4546388|1|110|+|AT1TE14750|ARNOLD2|DNA/MuDR"    
[4] "te|6753991|6754119|1|129|-|AT1TE21830|ATDNAI27T9A|DNA/MuDR"
[5] "te|10655834|10655963|1|130|+|AT1TE34455|ARNOLD1|DNA/MuDR"  
[6] "te|11660991|11661122|1|132|+|AT1TE37760|ATDNA2T9C|DNA/MuDR"

Examples one self-hit

families



df.query.tmp = df.query[(df.query$val.hits == 1),]

cnt.init = c(table(df.query$family))
cnt.tmp = c(table(df.query.tmp$family))

common_names <- intersect(names(cnt.init), names(cnt.tmp))
# Создание dataframe только для совпадающих имен
df_match <- data.frame(names = common_names, values.init = cnt.init[common_names], 
                       values.tmp = cnt.tmp[common_names])


gradient_colors <- c(discrete_rainbow(nrow(df_match)))
names(gradient_colors) = NULL


p = ggplot(df_match, aes(x = values.init, y = values.tmp, label = names, color = names)) +
  geom_point() +
  # geom_text(hjust = 0, vjust = 0) +
  ggrepel::geom_text_repel(max.overlaps = 20) +
  xlab("Initial counts") +
  ylab("Counts in \"1 self-hits\" category") +
  scale_x_log10() +
  scale_y_log10() +
  scale_color_manual(values = gradient_colors) +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


pdf(paste(path.figures, 'tes_self_scatter_1_selfhits_fam.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

subfamilies



df.query.tmp = df.query[(df.query$val.hits == 1) & (df.query$len >= 600),]

cnt.init = c(table(df.query$subfam))
cnt.tmp = c(table(df.query.tmp$subfam))

common_names <- intersect(names(cnt.init), names(cnt.tmp))
# Создание dataframe только для совпадающих имен
df_match <- data.frame(names = common_names, values.init = cnt.init[common_names], 
                       values.tmp = cnt.tmp[common_names])


# gradient_colors <- c(discrete_rainbow(nrow(df_match)))
names(gradient_colors) = NULL


p = ggplot(df_match, aes(x = values.init, y = values.tmp, label = names, color = names)) +
  geom_point() +
  # geom_text(hjust = 0, vjust = 0) +
  ggrepel::geom_text_repel(max.overlaps = 20) +
  xlab("Initial counts") +
  ylab("Counts in \"1 self-hits\" category") +
  scale_x_log10() +
  scale_y_log10() +
  # scale_color_manual(values = gradient_colors) +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


pdf(paste(path.figures, 'tes_self_scatter_1_selfhits_subfam.pdf', sep = ''), width = 7, height = 5)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

individuals from subfamilies

s.subfam = 'ATREP8'

df.query.tmp = df.query[(df.query$subfam == s.subfam) & (df.query$len >= 600),]
df.query.tmp

Creating the graph

# all edges
idx = res.nest$p1 >= sim.cutoff
edges = cbind(res.nest$V1[idx], res.nest$V8[idx])
idx = res.nest$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest$V8[idx], res.nest$V1[idx]))
te.enges.names = unique(c(edges[,1], edges[,2]))
te.enges.fam = sapply(te.enges.names, function(s) strsplit(s, '\\|')[[1]][9] )

te.enges.fam[te.enges.fam %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
te.enges.fam[te.enges.fam %in% c('RathE1_cons', 'RathE2_cons', 'RathE3_cons')] = 'RathE1/2/3_cons'
te.enges.fam[te.enges.fam %in% c('LINE/L1', 'LINE?')] = 'LINE'
te.enges.fam[te.enges.fam %in% c('Unassigned')] = 'Mix'
te.enges.fam[te.enges.fam %in% c('RC/Helitron')] = 'Helitron'

edges = edges[te.enges.fam[edges[,1]] != 'TEG',]
edges = edges[te.enges.fam[edges[,2]] != 'TEG',]
te.enges.names = unique(c(edges[,1], edges[,2]))


# nodes
idx = (res.nest$p1 >= sim.cutoff) & (res.nest$p8 >= sim.cutoff)
te.nodes = cbind(res.nest$V1[idx], res.nest$V8[idx])
te.nodes = te.nodes[te.enges.fam[te.nodes[,1]] != 'TEG',]
te.nodes = te.nodes[te.enges.fam[te.nodes[,2]] != 'TEG',]

te.rest = setdiff(te.enges.names, c(te.nodes[,1], te.nodes[,2]))


te.nodes.graph <- igraph::make_graph(t(te.nodes), directed = T)
te.nodes.graph <- igraph::simplify(te.nodes.graph)
te.nodes.comp <- igraph::components(te.nodes.graph)

nodes = data.frame(node = paste('N', te.nodes.comp$membership, sep = ''), 
                   te = names(te.nodes.comp$membership))

nodes.rest = data.frame(node = paste('R', (1:length(te.rest)), sep = ''), te = te.rest)
nodes = rbind(nodes, nodes.rest)

rownames(nodes) = nodes$te


nodes.cnt = data.frame(cnt = c(table(nodes$node)))
nodes.cnt$node = rownames(nodes.cnt)
nodes.cnt$fam = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  fam.te = unique(te.enges.fam[s.te])
  if(length(fam.te) == 1){
    return(fam.te)
  } else {
    fam.te = setdiff(fam.te, 'TEG')
    if(length(fam.te) == 1) return(fam.te)
    return('Mix')
  }
})
table(nodes.cnt$fam)

            DNA        DNA/MuDR        Helitron            LINE       LTR/Copia       LTR/Gypsy             Mix 
           1109            1228            2302             356             503            1837              67 
RathE1/2/3_cons            SINE 
             53              27 
# Redefine edges but with node names
idx.endes = (edges[,1] %in% nodes$te) & (edges[,2] %in% nodes$te)
b.graph = cbind(nodes[edges[idx.endes,1], 'node'],nodes[edges[idx.endes,2], 'node'])
b.graph = unique(b.graph)
# b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph.uni = b.graph[b.graph[,1] == b.graph[,2],]
b.graph = b.graph[b.graph[,1] != b.graph[,2],]

length(unique(c(b.graph[,1], b.graph[,2])))
[1] 7245
# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
[1] 1000
[1] 2000
[1] 3000
[1] 4000
[1] 5000
[1] 6000
[1] 7000
[1] 8000
[1] 9000
[1] 10000
[1] 11000
[1] 12000
[1] 13000
[1] 14000
[1] 15000
[1] 16000
[1] 17000
[1] 18000
[1] 19000
[1] 20000
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]
# b.graph = rbind(b.graph, b.graph.uni)

# Print graph

g.nodes.fam = nodes.cnt$fam
names(g.nodes.fam) = nodes.cnt$node
g.nodes.cnt = nodes.cnt$cnt
names(g.nodes.cnt) = nodes.cnt$node

g.cols = discrete_rainbow(length(unique(g.nodes.fam)))
names(g.cols) = unique(g.nodes.fam)

b.graph.init = b.graph


g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

Old colors

p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.fam[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) 
Loading required package: sna
Loading required package: statnet.common

Attaching package: ‘statnet.common’

The following objects are masked from ‘package:base’:

    attr, order

sna: Tools for Social Network Analysis
Version 2.7-1 created on 2023-01-24.
copyright (c) 2005, Carter T. Butts, University of California-Irvine
 For citation information, type citation("sna").
 Type help(package="sna") to get started.


Attaching package: ‘sna’

The following objects are masked from ‘package:igraph’:

    betweenness, bonpow, closeness, components, degree, dyad.census, evcent, hierarchy, is.connected,
    neighborhood, triad.census

Loading required package: scales

Attaching package: ‘scales’

The following object is masked from ‘package:viridis’:

    viridis_pal

Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'
p + guides(size = F)


# 
# b.graph.fam = cbind(g.nodes.fam[b.graph[,1]], g.nodes.fam[b.graph[,2]])
# b.graph.fam
# 
# which((b.graph.fam[,1] == 'DNA/MuDR') & (b.graph.fam[,1] == 'LINE'))

New Family colors

g.fam.names = sort(unique(g.nodes.fam))
fam.palette = c()
idx.pallete = c()

idx.fam <- grep("^Helitron", g.fam.names, value = FALSE)
tmp.palette <- colorRampPalette(c('#BFACE2', '#266D98', '#422B72'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

idx.fam <- grep("^LTR", g.fam.names, value = FALSE)
tmp.palette <- colorRampPalette(c('#BFDB38', '#54B435'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

idx.fam <- grep("^DNA", g.fam.names, value = FALSE)
tmp.palette <- colorRampPalette(c('#F9B5D0', '#971549'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

idx.fam = setdiff(1:length(g.fam.names), idx.pallete)
tmp.palette <- colorRampPalette(c('#FFC26F', '#C38154', '#884A39', '#4E3636'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

names(fam.palette) = g.fam.names[idx.pallete]
fam.palette['Unassigned'] = 'grey'
fam.palette['Mix'] = 'black'
fam.palette['TEG'] = 'darkgreen'

Separately visualise connected components

tmp.graph <- igraph::make_graph(t(b.graph), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

tmp.cnt = table(tmp.comp$membership)
tmp.cnt = -sort(-tmp.cnt)
head(tmp.cnt)

   2   51  209  176  104   29 
3493   40   38   37   32   31 
k = 1
tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.big <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.big = network.vertex.names(g.part.sub.big)


set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'
p.big.type = p + theme(legend.position = "none")

# set.seed(20)
# p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
#             node.size = g.nodes.cnt[b.graph.names.sub.big], 
#             color = g.nodes.fam[b.graph.names.sub.big],
#             mode = 'kamadakawai',
#             palette = fam.palette) + guides(size = F)
# p.big.color = p + theme(legend.position = "none")


tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership != tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.small <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.small = network.vertex.names(g.part.sub.small)


set.seed(20)
p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.fam[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'
p.small.type =p + theme(legend.position = "none")

# set.seed(20)
# p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
#             node.size = g.nodes.cnt[b.graph.names.sub.small], 
#             color = g.nodes.fam[b.graph.names.sub.small],
#             # mode = 'kamadakawai',
#             palette = fam.palette) + guides(size = F)
# p.small.color = p + theme(legend.position = "none")

Plots

p.big.type

p.small.type


pdf(paste(path.figures, 'graph_tes_family_small.pdf', sep = ''), width = 9, height = 9)
print(p.small.type)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 
pdf(paste(path.figures, 'graph_tes_family_big.pdf', sep = ''), width = 5, height = 5)
print(p.big.type)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Stop for the paper

stop()

Specific TE families

Graph of one family

sort(-table(df.query$subfam[(df.query$val.hits == 3) & (df.query$family == 'LTR/Copia')]))

     META1  ATCOPIA95  ATCOPIA57  ATCOPIA28  ATCOPIA41  ATCOPIA49  ROMANIAT5  ATCOPIA94  ATCOPIA13  ATCOPIA37 
       -58        -51        -34        -33        -28        -28        -28        -27        -23        -22 
 ATCOPIA65  ATCOPIA43  ATCOPIA35  ATCOPIA42  ATCOPIA69  ATCOPIA78  ATCOPIA27  ATCOPIA58      ATRE1  ATCOPIA29 
       -22        -21        -16        -15        -15        -15        -14        -14        -14        -13 
 ATCOPIA54  ATCOPIA45  ATCOPIA51  ATCOPIA66  ATCOPIA75  ATCOPIA12  ATCOPIA36  ATCOPIA50  ATCOPIA67   ENDOVIR1 
       -12        -11        -11        -11        -11        -10        -10        -10        -10        -10 
 ATCOPIA16  ATCOPIA21  ATCOPIA22  ATCOPIA34   ATCOPIA4  ATCOPIA44  ATCOPIA48  ATCOPIA62  ATCOPIA63  ATCOPIA64 
        -9         -9         -9         -9         -9         -9         -9         -9         -9         -9 
 ATCOPIA87  ATCOPIA11  ATCOPIA55  ATCOPIA61  ATCOPIA70  ATCOPIA15  ATCOPIA25  ATCOPIA30  ATCOPIA52  ATCOPIA93 
        -9         -8         -8         -8         -8         -7         -7         -7         -7         -7 
 ATCOPIA96  ATCOPIA26  ATCOPIA31  ATCOPIA56  ATCOPIA8A   ATCOPIA9   ATCOPIA1  ATCOPIA10  ATCOPIA23   ATCOPIA3 
        -7         -6         -6         -6         -6         -6         -5         -5         -5         -5 
ATCOPIA31A  ATCOPIA38  ATCOPIA40   ATCOPIA5  ATCOPIA83  ATCOPIA89  ATCOPIA91  ATCOPIA97  ATCOPIA14  ATCOPIA17 
        -5         -5         -5         -5         -5         -5         -5         -5         -4         -4 
  ATCOPIA2  ATCOPIA24  ATCOPIA32  ATCOPIA33 ATCOPIA38B  ATCOPIA39  ATCOPIA46  ATCOPIA68  ATCOPIA74  ATCOPIA88 
        -4         -4         -4         -4         -4         -4         -4         -4         -4         -4 
 ATCOPIA8B  ATCOPIA90  ATCOPIA19  ATCOPIA60  ATCOPIA72  ATCOPIA76  ATCOPIA77  ATCOPIA79  ATCOPIA82  ATCOPIA85 
        -4         -4         -3         -3         -3         -3         -3         -3         -3         -3 
 ATCOPIA86      TA1-2  ATCOPIA18 ATCOPIA18A  ATCOPIA20 ATCOPIA32B  ATCOPIA47  ATCOPIA53  ATCOPIA59 ATCOPIA65A 
        -3         -3         -2         -2         -2         -2         -2         -2         -2         -2 
 ATCOPIA71  ATCOPIA73  ATCOPIA81  ATCOPIA92 ATCOPIA38A   ATCOPIA6   ATCOPIA7  ATCOPIA80  ATCOPIA84 
        -2         -2         -2         -2         -1         -1         -1         -1         -1 

# one.te.fam = 'BRODYAGA1'
# one.te.fam = 'BRODYAGA2'
# one.te.fam = 'HELITRONY1D'
# one.te.fam = 'HELITRONY3'
one.te.fam = 'ATCOPIA41'
query.fam = df.query$query[df.query$subfam == one.te.fam]


one.te.fam = 'ATCOPIA41'
query.fam = df.query$query[df.query$subfam == one.te.fam]

res.nest.famp = res.nest[(res.nest$V1 %in% query.fam) | (res.nest$V8 %in% query.fam),]


idx = res.nest.famp$p1 >= sim.cutoff
edges = cbind(res.nest.famp$V1[idx], res.nest.famp$V8[idx])
idx = res.nest.famp$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest.famp$V8[idx], res.nest.famp$V1[idx]))


te.enges.names = unique(c(edges[,1], edges[,2]))
te.enges.fam = sapply(te.enges.names, function(s) strsplit(s, '\\|')[[1]][9] )
te.enges.fam[te.enges.fam %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
te.enges.fam[te.enges.fam %in% c('RathE1_cons', 'RathE2_cons', 'RathE3_cons')] = 'RathE1/2/3_cons'
te.enges.fam[te.enges.fam %in% c('LINE/L1', 'LINE?')] = 'LINE'
te.enges.fam[te.enges.fam %in% c('Unassigned')] = 'Mix'
te.enges.fam[te.enges.fam %in% c('RC/Helitron')] = 'Helitron'

g.part <- network(edges, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)
b.graph.len = as.numeric(sapply(strsplit(b.graph.names, "\\|"), function(x) x[5]))


label.family = sapply(strsplit(b.graph.names, "\\|"), function(x) x[8])
lab.cols = c('#3F2E3E', "white")
label.color = lab.cols[(label.family == one.te.fam) + 1]

set.seed(20)
p <- ggnet2(g.part, label = b.graph.len, edge.color = "black", 
             node.size = 15,
            alpha=0.8,
            arrow.gap = 0.015,
            arrow.size = 5,
            label.color = label.color,
            # node.size = g.nodes.cnt[b.graph.names], 
            color = te.enges.fam[b.graph.names],
            palette = fam.palette,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 

pdf(paste(path.figures, 'real_tes_subfam_', one.te.fam, '.pdf', sep = ''), width = 20, height = 18)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

set.seed(20)
p <- ggnet2(g.part, label = b.graph.names, edge.color = "black",
             node.size = 15,
            alpha=0.8,
            arrow.gap = 0.015,
            arrow.size = 5,
            # label.color = label.color,
            # node.size = g.nodes.cnt[b.graph.names],
            color = te.enges.fam[b.graph.names],
            palette = fam.palette,
            # mode = "kamadakawai"
            ) + guides(size = F)

pdf(paste(path.figures, 'real_tes_subfam_', one.te.fam, '_names.pdf', sep = ''), width = 50, height = 49)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

Dotplots

Functions

n.match = (nrow(result) + nrow(result.rc)) / length(seq1) / length(seq1)
Error in nrow(result.rc) : object 'result.rc' not found

Read TE sequences

file.te.fasta = '/Volumes/Samsung_T5/vienn/tair/new_filtration/new_te.fasta'
te.fasta = seqinr::read.fasta(file.te.fasta)
Warning: cannot open file '/Volumes/Samsung_T5/vienn/tair/new_filtration/new_te.fasta': No such file or directoryError in file(con, "r") : cannot open the connection

One pairwise example

one VS all


wsize = 10
nmatch = 8

name0 = 'te|11683565|11689821|3|6257|+|AT3TE48540|ATCOPIA95|LTR/Copia'
name0 = 'te|16691748|16695154|1|3407|−|AT1TE55070|ATCOPIA41|LTR/Copia'
name0 = gsub('−', "-", name0)


one.te.fam = strsplit(name0, '\\|')[[1]][8]
# one.te.fam = 'BRODYAGA2'
query.fam = df.query$query[df.query$subfam == one.te.fam]
query.fam = query.fam[(query.fam %in% res.nest.sim$V1) | (query.fam %in% res.nest.sim$V2)]

names.all = setdiff(query.fam, name0)

p.all = list()
for(name2 in names.all){
  # message(name2)
  seq1 = te.fasta[[name0]]
  seq2 = te.fasta[[name2]]
  
  s1 = strsplit(name0, '\\|')[[1]][7]
  s2 = strsplit(name2, '\\|')[[1]][7]
  p = dotplot(seq1, seq2, wsize, nmatch) + xlab(s1) + ylab(s2)
  p.all[[name2]] = p
}
# 
# pp = grid.arrange(grobs = p.all, ncol = 13) ## display plot
# 
# 
# pdf(paste(path.figures, 'pairwise_all','.pdf', sep = ''), width = 50, height = 50)
# print(pp)     # Plot 1 --> in the first page of PDF
# dev.off()

s0 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '_')
s0 = gsub("/", "-", s0)
pdf(paste(path.figures, 'pairwise_all_',s0,'.pdf', sep = ''), width = 50, height = 50)
grid.arrange(grobs = p.all, ncol = ceiling(sqrt(length(p.all)))) # Write the grid.arrange in the file
dev.off() # Close the file

one connected component


wsize = 10
nmatch = 8


name0 = 'te|6205621|6206184|2|564|−|AT2TE25255|HELITRONY1D|RC/Helitron'
name0 = 'te|14189256|14190266|5|1011|-|AT5TE50700|HELITRONY3|RC/Helitron'
name0 = 'te|12513239|12513824|1|586|+|AT1TE40725|ATHILA4A|LTR/Gypsy'
name0 = 'te|11647426|11648912|1|1487|+|AT1TE37705|ATREP7|RC/Helitron'
name0 = 'te|11683565|11689821|3|6257|+|AT3TE48540|ATCOPIA95|LTR/Copia'
name0 = gsub('−', "-", name0)


names.all = unique(c(res.nest.sim$V1[res.nest.sim$V8 == name0],
                     res.nest.sim$V8[res.nest.sim$V1 == name0]))
# names.all = unique(c(res.nest$V1[res.nest$V8 == name0], 
#                      res.nest$V8[res.nest$V1 == name0]))

p.all = list()
for(name2 in names.all){
  # message(name2)
  seq1 = te.fasta[[name0]]
  seq2 = te.fasta[[name2]]
  
  s1 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '|')
  s2 = paste0(strsplit(name2, '\\|')[[1]][7:9], collapse = '|')
  p = dotplot(seq1, seq2, wsize, nmatch) + xlab(s1) + ylab(s2)
  p.all[[name2]] = p
}


s0 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '_')
s0 = gsub("/", "-", s0)
pdf(paste(path.figures, 'pairwise_connect_',s0,'.pdf', sep = ''), width = 50, height = 50)
grid.arrange(grobs = p.all, ncol = ceiling(sqrt(length(p.all)))) # Write the grid.arrange in the file
dev.off() # Close the file


name1 = 'te|14189256|14190266|5|1011|-|AT5TE50700|HELITRONY3|RC/Helitron'
name2 = 'te|2162295|2162937|2|643|-|AT2TE09950|HELITRONY3|RC/Helitron'
name0 = name1

names.all = unique(c(res.nest$V1[res.nest$V8 == name0], res.nest$V8[res.nest$V1 == name0]))


names = c(name1, name2)
b.tmp = bl.res[(bl.res$V1 %in% names) & (bl.res$V8 %in% names),]

res.nest[(res.nest$V1 %in% names) & (res.nest$V8 %in% names), ]

SVs

Readings seSVs


sv.se = readRDS(paste(path.svs, 'sv_se.rds', sep = ''))

# Rename length groups
lev.replace = c('[1,10]', '(10,15]')
lev.new = '[1,15]'

s.levels = as.character(levels(sv.se$len.gr))
s.levels = s.levels[!(s.levels %in% lev.replace)]
s.levels = c(lev.new, s.levels)
s.levels = gsub("e\\+03", "k", s.levels)

sv.se$len.gr = as.character(sv.se$len.gr)
sv.se$len.gr[sv.se$len.gr %in% lev.replace] = lev.new
sv.se$len.gr = gsub("e\\+03", "k", sv.se$len.gr)
sv.se$len.gr = factor(sv.se$len.gr, levels = s.levels)


# Replace families
sv.se$fam = as.character(sv.se$fam)
sv.se$fam <- gsub("Helitron/.*", "Mix with Helitron", sv.se$fam)


sv.se$te = factor(sv.se$te, levels = c('isTE', 'isTEpart', 'hasTE', 'hasTEpart', 'noTE'))

Reading nestedness


# Load similarity function

file.nestedness = paste(path.work, 'sv_big_on_big_nest.rds', sep = '')


if(!file.exists(file.nestedness)){
  bl.file = paste(path.work, 'sv_big_on_big.txt', sep = '')
  bl.res = read.table(bl.file)
  bl.res = bl.res[bl.res$V1 != bl.res$V8,]

  res.nest = findNestedness(bl.res, use.strand = F)
    
  res.nest$len1 = res.nest.len[res.nest$V1]
  res.nest$len8 = res.nest.len[res.nest$V8]
  res.nest$p1 = res.nest$C1 / res.nest$len1
  res.nest$p8 = res.nest$C8 / res.nest$len8  
  saveRDS(res.nest, file.nestedness, compress = F)
} else {
  res.nest = readRDS(file.nestedness)
}

res.nest.len = sapply(unique(c(res.nest$V1, res.nest$V8)), 
                      function(s) as.numeric(strsplit(s, '\\|')[[1]][2]))
res.nest0 = res.nest

TE stat

In graph - not in graph

res.nest = res.nest0

sv.se.len = sv.se[sv.se$len >= 100,]
sv.se.len$in.connect = sv.se.len$name %in% names(res.nest.len)

cnt.sv.se = table(sv.se.len$in.connect , sv.se.len$te)
cnt.sv.se
       
        isTE isTEpart hasTE hasTEpart noTE
  FALSE   41      682   220       454 5615
  TRUE  4299     2627  2864      1468 2049
df = reshape2::melt(cnt.sv.se)

te.content.names = c("noTE", "isTE", "hasTE", "hasTEpart", "isTEpart")
cols = c('#D8D9CF', '#EB455F', '#7B6079', '#3C8DAD', '#79B773')
names(cols) = te.content.names

df$Var2 = factor(df$Var2, levels = rev(c('isTE', 'isTEpart', 'hasTE', 'hasTEpart', 'noTE')))


# install.packages("ggpattern")


p = ggplot(df, aes(x = Var2, y = value, fill = Var2, alpha = Var1, color = Var1)) +
  geom_col_pattern( aes(pattern = Var1),
    # pattern = rep(c('none', "stripe"), 5),
    pattern_density = 0.1,
    pattern_spacing = 0.025,
    pattern_fill = "grey70", 
    position = "dodge", 
    width = 0.8
  ) + 
  # geom_col(position = "dodge", width = 0.8) +
  scale_alpha_manual(values = c(0.8, 1), labels = c("No", "Yes")) +
  scale_color_manual(values = c('black', 'black'), labels = c("not in graph", "in graph")) +
  scale_pattern_manual(values = c("stripe", 'none'), labels = c("in graph", "not in graph"),
                       breaks = c(TRUE, FALSE)) +
  labs(fill = "", pattern='Connected to others') +
  scale_fill_manual(values = cols) +
  xlab(NULL) +
  ylab("Number of SVs") +
  theme(axis.text.y = element_blank()) + 
  guides(alpha = "none", fill = 'none', color = 'none') +
  theme_minimal() + coord_flip() +
  theme(
    legend.position = c(0.7, 0.3),     # Adjust these coordinates as needed
    legend.background = element_rect(fill="transparent", color='grey70')  
  ) +
  theme(axis.text.y = element_blank()) +
  guides(pattern = guide_legend(override.aes = list(fill = c("white"), color= 'black')))  
p

pdf(paste(path.figures, 'graph_mob_in_graph.pdf', sep = ''), width = 3, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

TE families in SV types

pp = ggpubr::ggarrange(p + xlab('TE content') + scale_x_discrete(labels = c('is compl.', 'is fragm.', 
                               'cont. compl.', 'cont. fragm.')) , g, ncol = 2, widths = c(0.75, 0.25))
pp

pdf(paste(path.figures, 'graph_mob_te_fam_sv_type.pdf', sep = ''), width = 6, height = 4)
print(pp)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

TE fam: TAIR10


p <- ggplot(df, aes(x = ref, y = value, color = names)) +
  geom_smooth(aes(group = 1), method = "lm", formula = y ~ x, se = FALSE, color = 'grey70') + 
  geom_point() +
  ggrepel::geom_text_repel(aes(label = names), max.overlaps = 20) +
  # xlab("log # in TAIR10 annotation") +
  # ylab("log # in SVs") +
  # scale_x_log10() +
  # scale_y_log10() +
  xlab("# in TAIR10 annotation") +
  ylab("# in SVs") +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


lm_model <- lm(value ~ ref, data = df)
slope <- coef(lm_model)[2]


p = p + annotate("text", x = min(df$ref), y = max(df$value), 
           label = paste('Slope:', round(slope, 3)), hjust = 0, vjust = 1)



pdf(paste(path.figures, 'graph_mob_te_fam_tair10.pdf', sep = ''), width = 4, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Filtration


res.nest = res.nest0

sv.names.mix = sv.se$name[grep("^Mix", sv.se$fam)]
res.nest = res.nest[!(res.nest$V1 %in% sv.names.mix),]
res.nest = res.nest[!(res.nest$V8 %in% sv.names.mix),]


sv.names.mix = sv.se$name[sv.se$te == 'noTE']
res.nest = res.nest[!(res.nest$V1 %in% sv.names.mix),]
res.nest = res.nest[!(res.nest$V8 %in% sv.names.mix),]

singleton.mode = F
if(singleton.mode){
  sv.names.freq = sv.se$name[sv.se$freq.max <= 3]
  # sv.names.freq = sv.se$name[sv.se$freq.max >= 25]
  res.nest = res.nest[res.nest$V1 %in% sv.names.freq,]
  res.nest = res.nest[res.nest$V8 %in% sv.names.freq,]
}

prefix.mode = c('', '_single')

Graph

# all edges
idx = res.nest$p1 >= sim.cutoff
edges = cbind(res.nest$V1[idx], res.nest$V8[idx])
idx = res.nest$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest$V8[idx], res.nest$V1[idx]))
te.enges.names = unique(c(edges[,1], edges[,2]))

tmp = sv.se$te
names(tmp) = sv.se$name
te.enges.type = as.character(tmp[te.enges.names])
names(te.enges.type) <- names(tmp[te.enges.names])


tmp = sv.se$fam
names(tmp) = sv.se$name
te.enges.fam = tmp[te.enges.names]

# nodes
idx = (res.nest$p1 >= sim.cutoff) & (res.nest$p8 >= sim.cutoff)
te.nodes = cbind(res.nest$V1[idx], res.nest$V8[idx])
te.rest = setdiff(te.enges.names, c(te.nodes[,1], te.nodes[,2]))


te.nodes.graph <- igraph::make_graph(t(te.nodes), directed = T)
te.nodes.graph <- igraph::simplify(te.nodes.graph)
te.nodes.comp <- igraph::components(te.nodes.graph)

nodes = data.frame(node = paste('N', te.nodes.comp$membership, sep = ''), te = names(te.nodes.comp$membership))

nodes.rest = data.frame(node = paste('R', (1:length(te.rest)), sep = ''), te = te.rest)
nodes = rbind(nodes, nodes.rest)

rownames(nodes) = nodes$te

# Define TE type
nodes.cnt = data.frame(cnt = c(table(nodes$node)))
nodes.cnt$node = rownames(nodes.cnt)
nodes.cnt$type = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  type.te = unique(te.enges.type[s.te])
  if(length(type.te) == 1){
    return(type.te)
  } else {
    type.te = table(type.te)
    type.te = names(type.te)[type.te == max(type.te)]
    return(type.te[1])
  }
})
table(nodes.cnt$type)

    hasTE hasTEpart      isTE  isTEpart 
     1043       466       433      1884 
# Define TE family
nodes.cnt$fam = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  type.te = unique(te.enges.fam[s.te])
  if(length(type.te) == 1){
    return(type.te)
  } else {
    type.te = table(type.te)
    type.te = names(type.te)[type.te == max(type.te)]
    return(type.te[1])
  }
})
table(nodes.cnt$fam)

        DNA/HAT        DNA/MuDR            DNA+        Helitron            LINE       LTR/Copia       LTR/Gypsy 
            127             697             356             818             441             442             702 
RathE1/2/3_cons            SINE             TEG      Unassigned 
             30              12             148              53 
# Redefine edges but with node names
idx.endes = (edges[,1] %in% nodes$te) & (edges[,2] %in% nodes$te)
b.graph = cbind(nodes[edges[idx.endes,1], 'node'],nodes[edges[idx.endes,2], 'node'])
b.graph = unique(b.graph)
# b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph.uni = b.graph[b.graph[,1] == b.graph[,2],]
b.graph = b.graph[b.graph[,1] != b.graph[,2],]

length(unique(c(b.graph[,1], b.graph[,2])))
[1] 3626
# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
[1] 1000
[1] 2000
[1] 3000
[1] 4000
[1] 5000
[1] 6000
[1] 7000
[1] 8000
[1] 9000
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]
# b.graph = rbind(b.graph, b.graph.uni)

# Print graph

g.nodes.type = nodes.cnt$type
names(g.nodes.type) = nodes.cnt$node
g.nodes.cnt = nodes.cnt$cnt
names(g.nodes.cnt) = nodes.cnt$node
g.nodes.fam = nodes.cnt$fam
names(g.nodes.fam) = nodes.cnt$node


g.cols.names = c("noTE", "isTE", "hasTE", "hasTEpart", "isTEpart")
g.cols = c('#FFD966', '#EB455F', '#7B6079', '#3C8DAD', '#79B773')
names(g.cols) = g.cols.names


g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.type[b.graph.names],
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = g.cols) + guides(size = F)
Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'
p

# path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

# set.seed(20)
# p <- ggnet2(g.part, label = F, edge.color = "grey30", 
#             node.size = g.nodes.cnt[b.graph.names], 
#             color = c('TE', 'noTE')[(g.nodes.type[b.graph.names] == 'noTE')*1+1],
#             # mode = 'kamadakawai',
#             # arrow.gap = 0, 
#             # arrow.size = 3,
#             palette = c('noTE' = 'black', 'TE' = '#AEC3AE')) + guides(size = F)
# p
# 
# # path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
# pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
#     width = 5, height = 5)
# print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
# dev.off()

Colored by TE family


if(length(setdiff(g.nodes.fam, names(fam.palette)))!=0) stop('not all families are defined')

set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "grey20", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.fam[b.graph.names],
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'
p = p + theme(legend.text = element_text(size = 8), 
          legend.title = element_blank(),
          legend.key.size = unit(0.5, "cm")) + guides(color = guide_legend(ncol = 2))
p


pdf(paste(path.figures, 'graph_mob_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 
pdf(paste(path.figures, 'graph_mob_cluster', prefix.mode[singleton.mode+1] ,'_family_legend.pdf', sep = ''), width = 7, height = 5)
print(p+ coord_fixed(ratio = 1))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Node size distribution

df = data.frame(node = unique(nodes$node))
df$size = g.nodes.cnt[df$node]
df$fam = g.nodes.fam[df$node]
df$type = g.nodes.type[df$node]

fam.palette
       Unassigned               Mix Mix with Helitron          Helitron         LTR/Copia         LTR/Gypsy 
           "grey"          "grey20"         "#266D98"         "#9581BC"         "#BFDB38"         "#54B435" 
          DNA/HAT              DNA+          DNA/MuDR              LINE   RathE1/2/3_cons              SINE 
        "#F9B5D0"         "#C8658C"         "#971549"         "#FFC26F"         "#C38154"         "#884A39" 
              TEG 
        "#4E3636" 
p = ggplot(df, aes(x = type, y = size, color=fam)) +
  geom_jitter(width = 0.2) +
  labs(x = "Type", y = "Size") + 
  scale_y_continuous(trans = "log2") +
  scale_color_manual(values = fam.palette)+
  theme_minimal() +
  guides(color = guide_legend(ncol = 2)) +
  labs(color = "TE family") + xlab('') + ylab('Node size (Number of similar SVs)')
p


pdf(paste(path.figures, 'graph_mob_size_distribution.pdf', sep = ''), width = 6.5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Separately visualise connected components

tmp.graph <- igraph::make_graph(t(b.graph), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

tmp.cnt = table(tmp.comp$membership)
tmp.cnt = -sort(-tmp.cnt)
head(tmp.cnt)

   1   33   14   28  126   22 
2161   28   27   25   22   21 
k = 1
tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.big <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.big = network.vertex.names(g.part.sub.big)


set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.type[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = g.cols) + guides(size = F)
Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'
p.big.type = p + theme(legend.position = "none")

set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'

Save

# p.big.type
# p.big.color
# p.small.type
# p.small.color


pdf(paste(path.figures, 'graph_mob_big_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 5, height = 5)
print(p.big.type)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_big_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), 
    width = 5, height = 5)
print(p.big.color)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_small_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 6, height = 6)
print(p.small.type)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_small_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), 
    width = 6, height = 6)
print(p.small.color)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 

Run by accessions

path.figures.acc = '/Volumes/Samsung_T5/vienn/work_te/figures_tegraph_accessions/'
sv.bin = read.table('/Volumes/Samsung_T5/vienn/work_sv/svs_se_bin_v03.txt', stringsAsFactors = F, check.names = FALSE)
# acc = '10002'

for(acc in colnames(sv.bin)){
  sv.acc = rownames(sv.bin)[sv.bin[,acc] == 1]
  rownames(sv.se) = sv.se$gr
  sv.acc = sv.se[sv.acc, 'name']
  
  sv.acc = intersect(sv.acc, rownames(nodes))
  nodes.cnt.acc = table(nodes[sv.acc,'node'])
  
  
  sv.alpha = rep(0, length(b.graph.names))
  names(sv.alpha) = b.graph.names
  sv.alpha[names(sv.alpha) %in% names(nodes.cnt.acc)] = 1
  
  # set.seed(239)
  # p <- ggnet2(g.part, label = F, edge.color = "black", 
  #             node.size = g.nodes.cnt[b.graph.names], 
  #             color = g.nodes.fam[b.graph.names],
  #             alpha = sv.alpha,
  #             # mode = 'kamadakawai',
  #             # arrow.gap = 0, 
  #             # arrow.size = 3,
  #             palette = fam.palette) + guides(size = F) + theme(legend.position = "none")
  
  set.seed(20)
  p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.fam[b.graph.names.sub.small],
            alpha = sv.alpha[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F) + theme(legend.position = "none")

  pdf(paste(path.figures.acc, 'graph_te', prefix.mode[singleton.mode+1] ,'_small_acc_',acc,'.pdf', sep = ''), width = 5, height = 5)
  print(p)     # Plot 1 --> in the first page of PDF
  dev.off()
  
  
  set.seed(20)
  p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            alpha = sv.alpha[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F) + theme(legend.position = "none")

  pdf(paste(path.figures.acc, 'graph_te', prefix.mode[singleton.mode+1] ,'_big_acc_',acc,'.pdf', sep = ''), width = 5, height = 5)
  print(p)     # Plot 1 --> in the first page of PDF
  dev.off()

}

p 
sv.annot = read.table('/Volumes/Samsung_T5/vienn/work_sv/svs_annotation_v03.txt', stringsAsFactors = F)
rownames(sv.annot) = sv.annot$gr
head(sv.annot)

sv.annot[extracted_values,]

Stop

stop()

Big TE-nodes

n.amount = 20

g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

size.big = g.nodes.cnt[b.graph.names]
alpha.big = rep(1, length(b.graph.names))
names(alpha.big) = b.graph.names
alpha.big[size.big < n.amount] = 0

sum(size.big >= n.amount)
[1] 25
set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = size.big, 
            color = g.nodes.fam[b.graph.names],
            alpha= alpha.big,
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = fam.palette) + guides(size = F) + guides(color = guide_legend(ncol = 2))
Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'
p

Which families specifically, and is the rate of insertion is different?

compare number of insertions with the total number of TE load


big.families = data.frame(node =  names(size.big)[size.big >= n.amount])
big.families$size = size.big[big.families$node]
big.families$fam = g.nodes.fam[big.families$node]
big.families = big.families[order(-big.families$size),]
rownames(big.families) = NULL

node.big = nodes[nodes$node %in% big.families$node,]

v = read.table(paste(path.work, 'blast_sv_on_tes.txt', sep = ''))
v = v[v$V1 %in% node.big$te,]


pos.len1 = 2
pos.len2 = 5
v1.len = sapply(unique(v$V1), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len1]))
v8.len = sapply(unique(v$V8), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len2]))
v.len = c(v1.len, v8.len)

v.sim = findNestedness(v, use.strand = F)
[1] 0
Error in `$<-.data.frame`(`*tmp*`, "overlap", value = 0) : 
  replacement has 1 row, data has 0

no-TE SV

Construct

sv.se = readRDS(paste(path.svs, 'sv_se.rds', sep = ''))
sim.cutoff = 0.85


sv.se.no.te = sv.se$name[(sv.se$te == 'noTE') & (sv.se$len > 50)]

bl.file = paste(path.work,'sv_big_on_big.txt', sep = '')
bl.sv = read.table(bl.file, stringsAsFactors = F)
bl.sv = bl.sv[bl.sv$V1 != bl.sv$V8,]

# remove having TEs
bl.sv = bl.sv[bl.sv$V1 %in% sv.se.no.te, ]
bl.sv = bl.sv[bl.sv$V8 %in% sv.se.no.te, ]

pos.len1 = 2
sv.len = sapply(unique(c(bl.sv$V1, bl.sv$V8)), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len1]))
bl.sv$len1 = sv.len[bl.sv$V1]
bl.sv$len8 = sv.len[bl.sv$V8]
max.len = 20000
bl.sv = bl.sv[(bl.sv$len1 <= max.len) & (bl.sv$len8 <= max.len),]
bl.sv$p1 = (bl.sv$V3 - bl.sv$V2 + 1) / bl.sv$len1
bl.sv$p8 = (abs(bl.sv$V5 - bl.sv$V4) + 1) / bl.sv$len8
bl.sv$comb = as.factor(paste(bl.sv$V1, bl.sv$V8, sep = '||'))

idx.mutual = (bl.sv$p1 >= sim.cutoff) & (bl.sv$p8 >= sim.cutoff)
# There is a big discussion in my head, whether it should be '&' or '|'
# If it's not ,utual, then maybe with something else it will construct a mutual relation, 
# so we should remain for the analysis of nestedness all partial inclusions
sv.mutual = bl.sv[idx.mutual, ]
v = bl.sv[!idx.mutual, ]
v = v[!(v$comb %in% sv.mutual$comb),]

# At some point it was a step to remain only those instances which are not "unique" in combinations
# but I think it's not correct here

sv.sim = findNestedness(v, use.strand = T)
[1] 437
[1] 12
[1] 1
[1] 0
[1] 440
[1] 11
[1] 1
[1] 0
sv.sim$p1 = sv.sim$C1 / sv.len[sv.sim$V1]
sv.sim$p8 = sv.sim$C8 / sv.len[sv.sim$V8]

# here  we should finally use '|', not '&'
sv.nested = sv.sim[(sv.sim$p1 >= sim.cutoff) | (sv.sim$p8 >= sim.cutoff) ,]

# Create pre-data for defining edges
common.names = intersect(colnames(sv.mutual), colnames(sv.nested))
sv.overall = rbind(sv.mutual[,common.names], sv.nested[,common.names])
sv.overall$group = (sv.overall$p1 >= sim.cutoff) * 1 + (sv.overall$p8 >= sim.cutoff) * 2
idx1 = sv.overall$group != 2  # V1 in V8
idx2 = sv.overall$group != 1  # V8 in V1


# Edges 
sv.edges = rbind(cbind(sv.overall$V1[idx1], sv.overall$V8[idx1]),
                 cbind(sv.overall$V8[idx2], sv.overall$V1[idx2]))


sv.graph <- igraph::make_graph(t(sv.edges), directed = T)
sv.graph <- igraph::simplify(sv.graph)
sv.graphcomp <- igraph::components(sv.graph)

sv.memb = data.frame(memb = sv.graphcomp$membership)
sv.memb$name = rownames(sv.memb)
rownames(sv.memb) = NULL
rownames(sv.se) = sv.se$name
sv.memb$te = sv.se[sv.memb$name, 'te']
sv.memb$cover = sv.se[sv.memb$name, 'cover'] / sv.se[sv.memb$name, 'len']
sv.memb$len = sv.len[sv.memb$name]

Plot all

g.part <- network(sv.edges, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = 1,
            # node.size = g.nodes.cnt[b.graph.names], 
            # color = g.nodes.type[b.graph.names],
            # palette = g.cols
            ) + guides(size = F)
p 

Plot with colors

p = p+ theme(legend.key.height = unit(0.5, "cm"))
p

pdf(paste(path.figures, 'graph_new_all.pdf', sep = ''), width = 6, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Types of the component


sv.graph <- igraph::make_graph(t(sv.edges), directed = T)
sv.graph <- igraph::simplify(sv.graph)
sv.graphcomp <- igraph::components(sv.graph)

sv.comp.member = sv.graphcomp$membership

s.tags = c("transpos","reverse","repeat","zinc", "receptor","defined prot", "undefined prot", 'no prot')
s.tags0 = rep('', length(s.tags))
s.tags0[1:4] = 'TE-like'
s.tags0[5:6] = 'Known Proteins'
s.tags0[7] = 'Undef. Proteins'
s.tags0[8] = 'No Proteins'
names(s.tags0) = s.tags

comp.tags = rep('', length(unique(sv.comp.member)))
for(s.tag in s.tags){
  tmp.tags = unique(sv.comp.member[names(g.nodes.prot)[g.nodes.prot == s.tag]])
  comp.tags[tmp.tags][comp.tags[tmp.tags] == ''] = s.tag
}
comp.tags[comp.tags == ''] = 'no prot'
comp.tags = data.frame(table(comp.tags))
colnames(comp.tags) = c('tag1', 'freq')
comp.tags$tag1 = factor(comp.tags$tag1, levels = s.tags)
comp.tags = comp.tags[order(comp.tags$tag1),]

comp.tags$tag0 = s.tags0[comp.tags$tag1]
comp.tags$tag0 = factor(comp.tags$tag0, levels = unique(s.tags0))

y.ticks = tapply(comp.tags$freq, comp.tags$tag0, sum)
y.ticks = y.ticks[!is.na(y.ticks)]

yy = sum(y.ticks) - cumsum(y.ticks) + y.ticks/2

comp.tags$ymin <- c(0, cumsum(comp.tags$freq)[-length(comp.tags$freq)])
comp.tags$ymax <- cumsum(comp.tags$freq)

x.step = rep(0, 8)
n.step = 10
x.step[c(5,7,8)] = n.step
x.step = cumsum(x.step)

comp.tags$ymin = comp.tags$ymin + x.step
comp.tags$ymax = comp.tags$ymax + x.step

y.min = tapply(comp.tags$ymin, comp.tags$tag0, min)
y.max = tapply(comp.tags$ymax, comp.tags$tag0, max)
y.val = (y.max + y.min) / 2
y.cnt = tapply(comp.tags$freq, comp.tags$tag0, sum)

df.text = data.frame(y.min = y.min, y.max = y.max, y.val = y.val, y.cnt = y.cnt, label = names(y.val))
df.text$angles <- 360 - (df.text$y.val / (max(comp.tags$ymax) + n.step)) * 360 
df.text$angles[2:3] = 180 + df.text$angles[2:3]

p = ggplot(comp.tags, aes(x = 0, y = freq, fill = tag1)) +
   geom_rect(aes(xmin = -0.5, xmax = 0.5, ymin = ymin, ymax = ymax)) +
   coord_polar("y", start = 0) +
   scale_fill_manual(values = g.cols.plus) + ylim(0, max(comp.tags$ymax) + n.step) +
   theme_void() + xlim(-1.5, 0.7) + 
   geom_text(data=df.text, aes(x = 0.7, y = y.val, label = paste(label, y.cnt, sep = ': ')), 
             angle = df.text$angles, inherit.aes = FALSE) +
  theme(legend.position="none")

p = p + annotate("text", x = -1.5, y = 0, label = paste('Total',sum(comp.tags$freq),'\n connected \ncomponents')) 

p

pdf(paste(path.figures, 'graph_new_pie_chart.pdf', sep = ''), width = 3.1, height = 3.1)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

I don’t know

sv.se$freq = sv.se$freq.max
n.cutoff = 3
n = 28
sv.se$sin = 'indel'
sv.se$sin[sv.se$freq >= (n - n.cutoff)] = 'deletion'
sv.se$sin[sv.se$freq <= n.cutoff] = 'insertion'


g.nodes.prot.sin = g.nodes.prot
g.nodes.prot.sin[names(g.nodes.prot.sin) %in% sv.se$name[sv.se$sin != 'insertion'] ] = 'na'
g.cols['na'] = 'white'




set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            color = g.nodes.prot.sin[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 

# 
# path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
# pdf(paste(path.figures, 'graph_sv_note_insertion.pdf', sep = ''), width = 6, height = 4)
# print(p)     # Plot 1 --> in the first page of PDF
# dev.off()


alpha.edta = rep(1, length(b.graph.names))
names(alpha.edta) = b.graph.names

sv.annot.adta = rowSums(sv.annot[,11:ncol(sv.annot)] > 0.7) > 0
sv.annot.adta = sv.annot.adta[sv.se$gr]
names(sv.annot.adta) = sv.se$name
sv.annot.adta = sv.annot.adta[sv.annot.adta]
alpha.edta[names(alpha.edta) %in% names(sv.annot.adta)] = 0


set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            alpha=1-alpha.edta,
            color = g.nodes.prot[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 


pdf(paste(path.figures, 'graph_mob_note_edta.pdf', sep = ''), width = 6, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_mob_note_edta_no_legend.pdf', sep = ''), width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()

Plot with component ID



tmp.graph <- igraph::make_graph(t(sv.edges), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

size.limit = 5
comp.id = as.character(tmp.comp$membership)
names(comp.id) = names(tmp.comp$membership)
comp.id[tmp.comp$csize[tmp.comp$membership] < size.limit] = ''

names.te = names(g.nodes.prot)[g.nodes.prot %in% c('transpos', 'reverse')]

comp.id[!(names(comp.id) %in% names.te)] = ''

comp.id[duplicated(comp.id)] = ''


comp.remain = as.numeric(comp.id[comp.id != ''])
alpha = rep(0, length(b.graph.names))
names(alpha) = names(tmp.comp$membership)
alpha[tmp.comp$membership %in% comp.remain] = 1

set.seed(239)
p <- ggnet2(g.part, label = comp.id[b.graph.names], 
            label.color = "black",
            label.size = 3,
            edge.color = "grey", 
            alpha = alpha[b.graph.names],
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            color = g.nodes.prot[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 


path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_sv_note_numbers.pdf', sep = ''), width = 5, height = 5)
print(p + theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()



# Order of components
cnt = table(tmp.comp$membership[tmp.comp$membership %in% comp.remain])
cnt = cnt[order(-cnt)]

CNV


cnv = readRDS('/Volumes/Samsung_T5/vienn/work_sv/similar_cnv_sv_on_accessions_cum_0.9.rds')

Plot one specific network


path.figures.examples  = '/Volumes/Samsung_T5/vienn/work_te/examples/'

# 
# tmp.graph <- igraph::make_graph(t(sv.edges), directed = T)
# tmp.graph <- igraph::simplify(tmp.graph)
# tmp.comp <- igraph::components(tmp.graph)
# 
# tmp.cnt = table(tmp.comp$membership)
# tmp.cnt = -sort(-tmp.cnt)

tmp.cnt = cnt

for(k in 1:length(tmp.cnt)){
  tmp.k = as.numeric(names(tmp.cnt)[k])
  tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
  b.graph.sub = sv.edges[(sv.edges[,1] %in% tmp.names) & 
                          (sv.edges[,2] %in% tmp.names),]
  
  
  g.part.sub <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
  b.graph.names.sub = network.vertex.names(g.part.sub)
  
  
    
  b.graph.size.sub <- as.numeric(sub(".*\\|", "", b.graph.names.sub))
  names(b.graph.size.sub) = b.graph.names.sub
  # b.graph.size.sub = ceiling(log(b.graph.size.sub, 10))
  
  if((length(unique( g.nodes.prot[b.graph.names.sub])) == 1)){
    set.seed(20)
    p <- ggnet2(g.part.sub, label = b.graph.size.sub[b.graph.names.sub], edge.color = "black", 
                node.size = 15,
                arrow.gap = 0.07, arrow.size = 3,
                color = g.cols[g.nodes.prot[b.graph.names.sub][1]],
                ) + guides(size = F) +  ggtitle(paste('Component #', tmp.k))
    p
  } else {
    set.seed(20)
    p <- ggnet2(g.part.sub, label = b.graph.size.sub[b.graph.names.sub], edge.color = "black", 
                node.size = 15,
                arrow.gap = 0.07, arrow.size = 3,
                color = g.nodes.prot[b.graph.names.sub],
                palette = g.cols,
                ) + guides(size = F) +  ggtitle(paste('Component #', tmp.k))
    p
  }
  
 
  
  pdf(paste(path.figures.examples, 'graph_sv_example_',k,'_comp_',tmp.k,'.pdf', sep = ''), width = 5, height = 4)
  print(p + theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
  dev.off()
  
  # annotation
  annot.tmp = sv.prot[sv.prot$name %in% b.graph.names.sub,]
  # annot.tmp = annot.tmp[annot.tmp$transpos == 1,]
  
  write.table(annot.tmp, paste(path.figures.examples, 'graph_sv_example_',k,'_pblast.txt', sep = ''), 
              row.names = F, col.names = F, quote = F, sep = '\t')
  
  
  # if EDTA annotation exists
  sv.tmp = unique(c(b.graph.sub))
  sv.tmp.cut <- gsub("\\|.*", "", sv.tmp)
  sv.annot.tmp = sv.annot[sv.tmp.cut,]
  n.fix = 9
  sv.annot.tmp  = sv.annot.tmp[,c(1:n.fix,n.fix+which(colSums(sv.annot.tmp[,(n.fix+1):ncol(sv.annot.tmp)]) != 0))]
  rownames(sv.annot.tmp) = sv.tmp
    
  write.table(sv.annot.tmp, paste(path.figures.examples, 'graph_sv_example_',k,'_edta.txt', sep = ''), 
             row.names = F, quote = F, sep = '\t')
  
  # Copy0Number variation
  cnv.tmp = cnv[sv.tmp,]
  
  heatmap(cnv.tmp, col = colorRampPalette(c("white", "red"))(20))
  
}

Pie-chart of proteins

library(ggplot2)

data <- data.frame(
  type = c("no proteins", "TE-related", "Категория 2", "Категория 3", "Категория 4"),
  value = c(135, 63, 85, 133)
)

pie.chart <- ggplot(data, aes(x = "", y = value, fill = type)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0) +
  theme_void()

pie.chart

Admixture groups

groups <- c(
  "germany",
  "south_sweden",
  "north_sweden",
  "south_sweden",
  "north_sweden",
  "germany",
  "western_europe",
  "central_europe",
  "italy_balkan_caucasus",
  "spain",
  "relict",
  "asia",
  "central_europe",
  "admixed",
  "spain",
  "relict",
  "italy_balkan_caucasus",
  "western_europe",
  "asia",
  "africa",
  "china",
  "china",
  "africa",
  "africa",
  "madeira",
  "madeira",
  "africa"
)

# Используем функцию table() для подсчета количества элементов в каждой группе
as.matrix(table(groups))

OLD

sunset <- colour("sunset")
discrete_rainbow <- colour("discrete rainbow")

file.te = '/Volumes/Samsung_T5/vienn/work/blast_tes_ann.txt'
sim.cutoff = 0.85
len.cutoff = 100

b = read.table(file.te, stringsAsFactors = F)
b = b[b$V1 != b$V8,]
b$len1 = as.numeric(sapply(b$V1, function(s) strsplit(s, '\\|')[[1]][7]))
b$len2 = as.numeric(sapply(b$V8, function(s) strsplit(s, '\\|')[[1]][7]))
b = b[b$len1 >= len.cutoff,]
b = b[b$len2 >= len.cutoff,]
b$comb = paste(b$V1, b$V8, sep = '^')

# Order positions in base
idx = b$V4 > b$V5
tmp = b[idx, 'V4']
b[idx, 'V4'] = b[idx, 'V5']
b[idx, 'V5'] = tmp

# --------------------------------------------------
# Get separately those, who has a unique coverage
comb.tbl = table(b$comb)
idx.uni = b$comb %in% names(comb.tbl)[comb.tbl == 1]
b.uni = b[idx.uni,]
b = b[!idx.uni,]

# This variable will be used later
b.uni$p1 = (b.uni$V3 - b.uni$V2 + 1) / b.uni$len1
b.uni$p2 = (b.uni$V5 - b.uni$V4 + 1) / b.uni$len2
b.uni = b.uni[(b.uni$p1 >= sim.cutoff) | (b.uni$p2 >= sim.cutoff),]

b.relations = data.frame(sub.te = b.uni$V1[b.uni$p1 >= sim.cutoff],
                         te = b.uni$V8[b.uni$p1 >= sim.cutoff], stringsAsFactors = F)
b.relations = rbind(b.relations,
                    data.frame(sub.te = b.uni$V8[b.uni$p2 >= sim.cutoff],
                               te = b.uni$V1[b.uni$p2 >= sim.cutoff], stringsAsFactors = F))
b.relations = unique(b.relations)

# --------------------------------------------------
# Min-max of the coverage to remove those, who are NOT in each other completely
b.cov = tapply(b$V2, b$comb, min)
b.cov = data.frame(comb = names(b.cov), V2 = b.cov)
b.cov$V3 = tapply(b$V3, b$comb, max)
b.cov$V4 = tapply(b$V4, b$comb, min)
b.cov$V5 = tapply(b$V5, b$comb, max)
b.cov$len1 = tapply(b$len1, b$comb, unique)
b.cov$len2 = tapply(b$len2, b$comb, unique)
b.cov$p1 = (b.cov$V3 - b.cov$V2 + 1) / b.cov$len1
b.cov$p2 = (b.cov$V5 - b.cov$V4 + 1) / b.cov$len2

comb.uncov = b.cov$comb[(b.cov$p1 < sim.cutoff) & (b.cov$p2 < sim.cutoff)]

b = b[!(b$comb %in% comb.uncov),]

# --------------------------------------------------
# Calculate the coverage directly for the first
b = b[order(b$V3),]
b = b[order(b$V2),]
b = b[order(b$comb),]

# Remove nested
idx = which((b$V3[-nrow(b)] > b$V3[-1]) & (b$comb[-nrow(b)] == b$comb[-1])) + 1
b1 = b[-idx,]

# Compute gaps
b1$gap = c(b1$V2[-1] - b1$V3[-nrow(b1)] - 1, 0)
b1$gap[b1$gap < 0] = 0
idx.diff.comb = which(b1$comb[-1] != b1$comb[-nrow(b1)])
b1$gap[idx.diff.comb] = 0

b.cov = tapply(b1$V2, b1$comb, min)
b.cov = data.frame(comb = names(b.cov), V2 = b.cov)
b.cov$V3 = tapply(b1$V3, b1$comb, max)
b.cov$len1 = tapply(b1$len1, b1$comb, unique)
b.cov$gap = tapply(b1$gap, b1$comb, sum)
b.cov$len1 = b.cov$len1 
b.cov$p1 = (b.cov$V3 - b.cov$V2 + 1 - b.cov$gap) / b.cov$len1
b.cov$V1 = tapply(b1$V1, b1$comb, unique)
b.cov$V8 = tapply(b1$V8, b1$comb, unique)

b.cov = b.cov[b.cov$p1 >= sim.cutoff,]


b.relations = rbind(b.relations,
                    data.frame(sub.te = b.cov$V1,
                               te = b.cov$V8, stringsAsFactors = F))


# --------------------------------------------------
# Calculate the coverage directly for the second
b = b[order(b$V5),]
b = b[order(b$V4),]
b = b[order(b$comb),]

# Remove nested
idx = which((b$V5[-nrow(b)] > b$V5[-1]) & (b$comb[-nrow(b)] == b$comb[-1])) + 1
b1 = b[-idx,]

# Compute gaps
b1$gap = c(b1$V4[-1] - b1$V5[-nrow(b1)] - 1, 0)
b1$gap[b1$gap < 0] = 0
idx.diff.comb = which(b1$comb[-1] != b1$comb[-nrow(b1)])
b1$gap[idx.diff.comb] = 0

b.cov = tapply(b1$V4, b1$comb, min)
b.cov = data.frame(comb = names(b.cov), V4 = b.cov)
b.cov$V5 = tapply(b1$V5, b1$comb, max)
b.cov$len2 = tapply(b1$len2, b1$comb, unique)
b.cov$gap = tapply(b1$gap, b1$comb, sum)
b.cov$len2 = b.cov$len2 
b.cov$p1 = (b.cov$V5 - b.cov$V4 + 1 - b.cov$gap) / b.cov$len2
b.cov$V1 = tapply(b1$V1, b1$comb, unique)
b.cov$V8 = tapply(b1$V8, b1$comb, unique)

b.cov = b.cov[b.cov$p1 >= sim.cutoff,]


b.relations = rbind(b.relations,
                    data.frame(sub.te = b.cov$V8,
                               te = b.cov$V1, stringsAsFactors = F))

  
b.relations = unique(b.relations)


b.relations

Define clusters

b.nodes = rbind(b.relations,
                    data.frame(sub.te = b.relations$te,
                               te = b.relations$sub.te))

b.nodes$comb = paste(b.nodes$sub.te, b.nodes$te, sep = '^')

comb.tbl = table(b.nodes$comb)
comb.back.and.foth = names(comb.tbl)[comb.tbl >= 2]
b.nodes = b.nodes[b.nodes$comb %in% comb.back.and.foth,]
b.nodes = unique(b.nodes[, c('sub.te', 'te')])


te.nodes <- igraph::make_graph(t(b.nodes), directed = T)
te.nodes <- igraph::simplify(te.nodes)
te.nodes.comp <- igraph::components(te.nodes)

nodes = paste('N', te.nodes.comp$membership, sep = '')
names(nodes) = names(te.nodes.comp$membership)

Identify family for each node


nodes.family = sapply(names(nodes), function(s) strsplit(s, '\\|')[[1]][6])

nodes.family.max = tapply(nodes.family, nodes, function(s){
  tbl = table(s)
  f = names(tbl)[tbl == max(tbl)]
  if(length(f) == 1){
    return(f)
  } else {
    return('Mix')
  }
})

nodes.family.max[nodes.family.max %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
nodes.family.max[nodes.family.max %in% c('RathE1_cons', 'RathE2_cons')] = 'DNA'
nodes.family.max[nodes.family.max %in% c('LINE/L1', 'LINE?')] = 'LINE'
nodes.family.max[nodes.family.max %in% c('Unassigned')] = 'Mix'
nodes.family.unique = unique(nodes.family.max)

Graph without singletons


b.graph.init = b.relations[(b.relations$sub.te %in% names(nodes)) & (b.relations$te %in% names(nodes)),]
b.graph = b.graph.init
b.graph = cbind(nodes[as.character(b.graph$sub.te)], nodes[as.character(b.graph$te)])
b.graph = unique(b.graph)


b.graph = b.graph[b.graph[,1] != b.graph[,2],]

# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]


# te.graph <- igraph::make_graph(t(b.graph), directed = T)
# te.graph <- igraph::simplify(te.graph)
# te.graph.comp <- igraph::components(te.graph)


nodes.family.max.graph = nodes.family.max[names(nodes.family.max) %in% unique(c(b.graph[,1], b.graph[,2]))]

graph.cols = sunset(length(unique(nodes.family.max.graph)))

graph.cols = discrete_rainbow(length(unique(nodes.family.max.graph)))
names(graph.cols) = unique(nodes.family.max.graph)
g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
p <- ggnet2(g.part, label = FALSE, edge.color = "black", node.size = 1, 
            color = nodes.family.max.graph, palette = graph.cols,
            mode = "kamadakawai")# + guides(size = FALSE)
p

Graph WITH singletons



names.core = names(nodes.family.max.graph)

b.graph.init = b.relations
for(i in 1:2){
  b.graph.init[b.graph.init[,i] %in% names(nodes), i] = nodes[b.graph.init[b.graph.init[,i] %in% names(nodes), i]]
}

b.graph = unique(b.graph.init)
b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph = unique(b.graph)
# Verteces from the previous graph
b.graph = b.graph[(b.graph[,1] %in% names.core) | (b.graph[,2] %in% names.core),]


# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]

te.graph <- igraph::make_graph(t(b.graph), directed = T)
d <- igraph::distances(te.graph)
# te.graph <- igraph::simplify(te.graph)
# te.graph.comp <- igraph::components(te.graph)

names.new = unique(setdiff(c(b.graph[,1], b.graph[,2]), names(nodes.family.max)))
# names.new.val = paste('G',1:length(names.new), sep = '')
# names(names.new.val) = names.new
# names.new.val = 

names.new.family = sapply(names.new, function(s) strsplit(s, '\\|')[[1]][6])
names.new.family[names.new.family %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
names.new.family[names.new.family %in% c('RathE1_cons', 'RathE2_cons')] = 'DNA'
names.new.family[names.new.family %in% c('LINE/L1', 'LINE?')] = 'LINE'
names.new.family[names.new.family %in% c('Unassigned')] = 'Mix'


nodes.family.max.add = c(nodes.family.max, names.new.family)
nodes.family.max.add = nodes.family.max.add[unique(c(b.graph[,1], b.graph[,2]))]

graph.cols = discrete_rainbow(length(unique(nodes.family.max.add)))
graph.cols = sample(graph.cols)
names(graph.cols) = unique(nodes.family.max.add)

g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
p <- ggnet2(g.part, label = FALSE, edge.color = "black", node.size = 0.5, 
            color = nodes.family.max.add,
            palette = graph.cols, mode = "kamadakawai")
p

TSNE



library(Rtsne)




d <- igraph::distances(te.graph)
d.max = max(d[!is.infinite(d)])

d[is.infinite(d)] = d.max * 1.3

tSNE <- Rtsne(d, is_distance = TRUE, dims = 2)

plot(tSNE$Y[,1], tSNE$Y[,2])
LS0tCnRpdGxlOiAiR3JhcGggb2YgVEVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKIyBTZXR1cApgYGB7ciwgbWVzc2FnZT1GQUxTRX0KIyBsaWJyYXJ5KGdncGxvdDIpCiMgbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShnZ3BhdHRlcm4pCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShjb2xvclJhbXBzKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KCdpZ3JhcGgnKQpsaWJyYXJ5KGdnbmV0KQpsaWJyYXJ5KG5ldHdvcmspCmxpYnJhcnkoa2hyb21hKQpsaWJyYXJ5KGRwbHlyKQoKc291cmNlKCdzaW1pbGFyaXR5LlInKQpzb3VyY2UoJ3NlcWRvdHBsb3QuUicpCgpzdW5zZXQgPC0gY29sb3VyKCJzdW5zZXQiKQpkaXNjcmV0ZV9yYWluYm93IDwtIGNvbG91cigiZGlzY3JldGUgcmFpbmJvdyIpCgpwYXRoLmJhc2UgPSAnLi4vLi4vLi4vJwpwYXRoLndvcmsgPSBwYXN0ZShwYXRoLmJhc2UsICcwMl9hbmFseXNpcy8wNF9zdi8wMV9kYXRhLycsIHNlcCA9ICcnKQpwYXRoLnRhaXIgPSBwYXN0ZShwYXRoLmJhc2UsICcwMV9kYXRhX2NvbW1vbi8wMV90YWlyMTAvJywgc2VwID0gJycpCnBhdGguZmlndXJlcyA9IHBhc3RlKHBhdGguYmFzZSwgJzAyX2FuYWx5c2lzLzA0X3N2LzAzX2ZpZ3VyZXMvJywgc2VwID0gJycpCnBhdGguc3ZzID0gcGFzdGUocGF0aC5iYXNlLCAnMDFfZGF0YV9jb21tb24vMDJfYW5ub3RfZGVub3ZvLzAyX3Bhbm5hZ3JhbS9zdnMvJywgc2VwID0gJycpCiMgcGF0aC5nZW5lcyA9IHBhc3RlKHBhdGguYmFzZSwgJzAxX2RhdGFfY29tbW9uLzAyX2Fubm90X2Rlbm92by8wMl9wYW5uYWdyYW0vZ2VuZXMvJywgc2VwID0gJycpCgojIHNpbS5jdXRvZmYgPSAwLjkKCnNpbS5jdXRvZmYgPSAwLjg1CgpgYGAKCiMgQ29vbG9ycwpgYGB7cn0KCgpmYW0ucGFsZXR0ZSA9IGMoKQpmYW0ucGFsZXR0ZVsnVW5hc3NpZ25lZCddID0gJ2dyZXknCmZhbS5wYWxldHRlWydNaXgnXSA9ICdncmV5MjAnCmZhbS5wYWxldHRlWydNaXggd2l0aCBIZWxpdHJvbiddID0gJyMyNjZEOTgnCmZhbS5wYWxldHRlWydIZWxpdHJvbiddID0gJyNCQ0FDREUnCmZhbS5wYWxldHRlWyJMVFIvQ29waWEiXSA9ICcjQkZEQjM4JwpmYW0ucGFsZXR0ZVsiTFRSL0d5cHN5Il0gPSAnIzU0QjQzNScKZmFtLnBhbGV0dGVbIkROQS9IQVQiXSA9ICcjRjlCNUQwJwpmYW0ucGFsZXR0ZVsiRE5BKyJdID0gJyNDODY1OEMnCmZhbS5wYWxldHRlWyJETkEvTXVEUiJdID0gJyM5NzE1NDknCgoKZmFtLnBhbGV0dGVbIkxJTkUiXSA9ICcjRkZDMjZGJwpmYW0ucGFsZXR0ZVsiUmF0aEUxLzIvM19jb25zIl0gPSAnI0MzODE1NCcKZmFtLnBhbGV0dGVbIlNJTkUiXSA9ICcjODg0QTM5JwpmYW0ucGFsZXR0ZVsiVEVHIl0gPSAnIzRFMzYzNicKCmBgYAoKCgojIFRFcwpgYGB7cn0KCiMgTG9hZCBzaW1pbGFyaXR5IGZ1bmN0aW9uCgpibC5maWxlID0gcGFzdGUocGF0aC53b3JrLCduZXdfdGVfb25fdGUuZmFzdGEnLHNlcCA9ICcnKQpibC5yZXMgPSByZWFkLnRhYmxlKGJsLmZpbGUpCmJsLnJlcyA9IGJsLnJlc1tibC5yZXMkVjEgIT0gYmwucmVzJFY4LF0KCmJsLnJlcy5pbml0ID0gYmwucmVzCmJsLnJlcyA9IGJsLnJlc1tibC5yZXMkVjYgPj0gc2ltLmN1dG9mZiAqIDEwMCxdCgpyZXMubmVzdCA9IGZpbmROZXN0ZWRuZXNzKGJsLnJlcywgdXNlLnN0cmFuZCA9IEYpCgpyZXMubmVzdC5sZW4gPSBzYXBwbHkodW5pcXVlKGMocmVzLm5lc3QkVjEsIHJlcy5uZXN0JFY4KSksIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bNV0pKQogIApyZXMubmVzdCRsZW4xID0gcmVzLm5lc3QubGVuW3Jlcy5uZXN0JFYxXQpyZXMubmVzdCRsZW44ID0gcmVzLm5lc3QubGVuW3Jlcy5uZXN0JFY4XQpyZXMubmVzdCRwMSA9IHJlcy5uZXN0JEMxIC8gcmVzLm5lc3QkbGVuMQpyZXMubmVzdCRwOCA9IHJlcy5uZXN0JEM4IC8gcmVzLm5lc3QkbGVuOAoKcmVzLm5lc3Quc2ltID0gcmVzLm5lc3RbKHJlcy5uZXN0JHAxID49IHNpbS5jdXRvZmYpIHwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgKHJlcy5uZXN0JHA4ID49IHNpbS5jdXRvZmYpLF0KYGBgCgojIyBIb3cgbWFueSBURXMgYXJlIGluIHRoZSBncmFwaApEaXN0cmlidXRpb24gYW1vbmcgZmFtaWxpZXMgYW5kIHN1YmZhbWlsaWVzCkRpc3RyaWJ1dGlvbiBhbW9uZyBsZW5ndGhzCmBgYHtyfQp0ZS5pbi5ncmFwaCA9IHVuaXF1ZShjKHJlcy5uZXN0JFYxLCByZXMubmVzdCRWOCkpCgojIFdoYXQgaXMgdGhlIGFjdHVhbCBudW1iZXIgb2YgVEVzCmZpbGUuY29udGVudCA8LSByZWFkTGluZXMoYmwuZmlsZSkKCnNlbGVjdGVkLmxpbmVzIDwtIGZpbGUuY29udGVudFtncmVwbCgiXiMgUXVlcnk6fGhpdHMgZm91bmQiLCBmaWxlLmNvbnRlbnQpXQpkZi5xdWVyeSA9IGRhdGEuZnJhbWUoYi5xdWVyeT1zZWxlY3RlZC5saW5lc1tzZXEoMSwgbGVuZ3RoKHNlbGVjdGVkLmxpbmVzKSwgYnkgPSAyKV0sCiAgICAgICAgICAgICAgICAgICAgICBiLmhpdHM9c2VsZWN0ZWQubGluZXNbc2VxKDIsIGxlbmd0aChzZWxlY3RlZC5saW5lcyksIGJ5ID0gMildKQoKZGYucXVlcnkkcXVlcnkgIDwtIGdzdWIoIl4jIFF1ZXJ5OiAoLiopIiwgIlxcMSIsIGRmLnF1ZXJ5JGIucXVlcnkpCmRmLnF1ZXJ5JGxlbiA8LSBhcy5udW1lcmljKHNhcHBseShzdHJzcGxpdChkZi5xdWVyeSRxdWVyeSwgIlxcfCIpLCBmdW5jdGlvbih4KSB4WzVdKSkKZGYucXVlcnkkaGl0cyA8LSBhcy5udW1lcmljKHN0cmluZ3I6OnN0cl9leHRyYWN0KGRmLnF1ZXJ5JGIuaGl0cywgIlxcZCsiKSkKZGYucXVlcnkkdmFsLmhpdHMgPSBkZi5xdWVyeSRoaXRzCmRmLnF1ZXJ5JHZhbC5oaXRzW2RmLnF1ZXJ5JHZhbC5oaXRzID49IDJdID0gMgpkZi5xdWVyeSR2YWwuaGl0c1tkZi5xdWVyeSRxdWVyeSAlaW4lIGJsLnJlcyRWOF0gPSAyCmRmLnF1ZXJ5JHZhbC5oaXRzW2RmLnF1ZXJ5JHF1ZXJ5ICVpbiUgdGUuaW4uZ3JhcGhdID0gMwpoaXQudmFsdWVzID0gYygnMCBoaXRzJywgJzEgc2VsZi1oaXQnLCAncGFydGlhbCBvdmVybGFwJywgJ2luIGdyYXBoJywgImluIGdyYXBoIGJ1dCBub3QgaW4gU1ZzIikKZGYucXVlcnkkcy5oaXRzID0gaGl0LnZhbHVlc1tkZi5xdWVyeSR2YWwuaGl0cysxXQpkZi5xdWVyeSRzLmhpdHMgPSBmYWN0b3IoZGYucXVlcnkkcy5oaXRzLCBsZXZlbHMgPSByZXYoaGl0LnZhbHVlcykpCmRmLnF1ZXJ5JGZhbWlseSA8LSBzYXBwbHkoc3Ryc3BsaXQoZGYucXVlcnkkcXVlcnksICJcXHwiKSwgZnVuY3Rpb24oeCkgeFs5XSkKZGYucXVlcnkkc3ViZmFtIDwtIHNhcHBseShzdHJzcGxpdChkZi5xdWVyeSRxdWVyeSwgIlxcfCIpLCBmdW5jdGlvbih4KSB4WzhdKQoKCm15X2NvbG9ycyA8LSBjb2xvcnMgPC0gYygiaW4gZ3JhcGgiID0gIiM2NzZGQTMiLAogICAgICAgICAgICAicGFydGlhbCBvdmVybGFwIiA9ICIjRkY5RjI5IiwKICAgICAgICAgICAgIjEgc2VsZi1oaXQiID0gIiM2RUJGOEIiLAogICAgICAgICAgICAiMCBoaXRzIiA9ICIjRDgyMTQ4IiwKICAgICAgICAgICAgImluIGdyYXBoIGJ1dCBub3QgaW4gU1ZzIiA9ICIjMTUxRDNCIikKCgojIFRFcywgd2hpY2ggYXJlIG5vdCBpbiBTVnMKdGUuaW4uc3ZzID0gcmVhZC50YWJsZShwYXN0ZShwYXRoLndvcmssICdibGFzdF90ZXNfb25fc3YudHh0Jywgc2VwID0gJycpLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKdGUucmVzdCA9IHNldGRpZmYoZGYucXVlcnkkcXVlcnksIHRlLmluLnN2cyRWMSkKdGUuaW4uc3ZzID0gcmVhZC50YWJsZShwYXN0ZShwYXRoLndvcmssICdibGFzdF9zdl9vbl90ZXMudHh0Jywgc2VwID0gJycpLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKdGUucmVzdCA9IHNldGRpZmYodGUucmVzdCwgdGUuaW4uc3ZzJFY4KQpkZi5xdWVyeSRzLmhpdHNbZGYucXVlcnkkcXVlcnkgJWluJSB0ZS5yZXN0XSA9ICJpbiBncmFwaCBidXQgbm90IGluIFNWcyIKCgpwID0gZ2dwbG90KGRmLnF1ZXJ5LCBhZXMoeCA9IGxlbiwgZmlsbCA9IHMuaGl0cywgY29sb3IgPSBzLmhpdHMpKSArCiAgIyBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uKSwgYWxwaGE9MC41LCBjb2xvciA9ICJibGFjayIsIGJpbnMgPSAzMCkgKwogICMgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMC4wMiwgd2lkdGggPSAwLCBhbHBoYSA9IDAuNykgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG15X2NvbG9ycykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMpICsKICAgc2NhbGVfeF9sb2cxMCgpICsKICBsYWJzKGZpbGwgPSBOVUxMLCBjb2xvciA9IE5VTEwpICsKICB4bGFiKCdsZW5ndGggb2YgVEVzJykgKyB5bGFiKCdOb3JtYWxpc2VkIGRlbnNpdHknKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDEsIDEpLCBsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMSwgMSksCiAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmV5OTAiKSkKCnAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICd0ZXNfc2VsZl9ibGFzdF9sZW5fZGVuc2l0eS5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKdGFibGUoZGYucXVlcnkkdmFsLmhpdHMpCmBgYAoKCiMjIyBURXMgbm90IGluIFNWcwpgYGB7cn0KIyBURXMgaW4gdGUtZ3JhcGg6IHRlLmluLmdyYXBoCiMgVEVzIHdoaWNoIGFyZSBoYXZlIG5vIGNvbm5lY3Rpb24gdG8gU1ZzCgpkZiA9IGFzLmRhdGEuZnJhbWUodGFibGUoZGYucXVlcnkkcy5oaXRzKSkKCiAgCmNvbG9ycyA8LSBjKCJpbiBncmFwaCIgPSAiIzY3NkZBMyIsCiAgICAgICAgICAgICJwYXJ0aWFsIG92ZXJsYXAiID0gIiNGRjlGMjkiLAogICAgICAgICAgICAiMSBzZWxmLWhpdCIgPSAiIzZFQkY4QiIsCiAgICAgICAgICAgICIwIGhpdHMiID0gIiNEODIxNDgiLAogICAgICAgICAgICAiaW4gZ3JhcGggYnV0IG5vdCBpbiBTVnMiID0gIiMxNTFEM0IiKQoKCnAgPSBnZ3Bsb3QoZGYsIGFlcyh4ID0gIiIsIHkgPSBGcmVxLCBmaWxsID0gVmFyMSkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTEsIGFscGhhID0gMC43KSArCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydD0wKSArCiAgbGFicyh0aXRsZT1OVUxMLCBmaWxsPSJDYXRlZ29yaWVzIikgKwogIHRoZW1lX3ZvaWQoKSsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBGcmVxLHggPSAxLjMpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3Rlc19zZWxmX2JsYXN0X3BpZV9jaGFydC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gMywgaGVpZ2h0ID0gMykKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKCgoKYGBgCgojIyBFeGFtcGxlcwojIyMgRXhhbXBsZXMgbm8gaGl0cwpgYGB7cn0KCgpkZi5xdWVyeS50bXAgPSBkZi5xdWVyeVsoZGYucXVlcnkkdmFsLmhpdHMgPT0gJzAnKSxdCgpjbnQuaW5pdCA9IGModGFibGUoZGYucXVlcnkkZmFtaWx5KSkKY250LnRtcCA9IGModGFibGUoZGYucXVlcnkudG1wJGZhbWlseSkpCgpjb21tb25fbmFtZXMgPC0gaW50ZXJzZWN0KG5hbWVzKGNudC5pbml0KSwgbmFtZXMoY250LnRtcCkpCiMg0KHQvtC30LTQsNC90LjQtSBkYXRhZnJhbWUg0YLQvtC70YzQutC+INC00LvRjyDRgdC+0LLQv9Cw0LTQsNGO0YnQuNGFINC40LzQtdC9CmRmX21hdGNoIDwtIGRhdGEuZnJhbWUobmFtZXMgPSBjb21tb25fbmFtZXMsIHZhbHVlcy5pbml0ID0gY250LmluaXRbY29tbW9uX25hbWVzXSwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzLnRtcCA9IGNudC50bXBbY29tbW9uX25hbWVzXSkKCgpncmFkaWVudF9jb2xvcnMgPC0gYyhkaXNjcmV0ZV9yYWluYm93KG5yb3coZGZfbWF0Y2gpKSkKbmFtZXMoZ3JhZGllbnRfY29sb3JzKSA9IE5VTEwKCgpwID0gZ2dwbG90KGRmX21hdGNoLCBhZXMoeCA9IHZhbHVlcy5pbml0LCB5ID0gdmFsdWVzLnRtcCwgbGFiZWwgPSBuYW1lcywgY29sb3IgPSBuYW1lcykpICsKICBnZW9tX3BvaW50KCkgKwogICMgZ2VvbV90ZXh0KGhqdXN0ID0gMCwgdmp1c3QgPSAwKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKG1heC5vdmVybGFwcyA9IDIwKSArCiAgeGxhYigiSW5pdGlhbCBjb3VudHMiKSArCiAgeWxhYigiY291bnRzIG9mIE5vIGhpdHMiKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBncmFkaWVudF9jb2xvcnMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIHRoZW1lX21pbmltYWwoKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3Rlc19zZWxmX3NjYXR0ZXJfbm9faGl0cy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgojIE5vIGhpdHMgYW5kIGxvbmcKbGVuLm1pbiA9IDEwMApkZi5xdWVyeS50bXAgPSBkZi5xdWVyeVsoZGYucXVlcnkkdmFsLmhpdHMgPT0gJzAnKSAmIChkZi5xdWVyeSRsZW4gPj0gbGVuLm1pbiksXQoKCmNudC5pbml0ID0gYyh0YWJsZShkZi5xdWVyeSRmYW1pbHkpKQpjbnQudG1wID0gYyh0YWJsZShkZi5xdWVyeS50bXAkZmFtaWx5KSkKCmNvbW1vbl9uYW1lcyA8LSBpbnRlcnNlY3QobmFtZXMoY250LmluaXQpLCBuYW1lcyhjbnQudG1wKSkKIyDQodC+0LfQtNCw0L3QuNC1IGRhdGFmcmFtZSDRgtC+0LvRjNC60L4g0LTQu9GPINGB0L7QstC/0LDQtNCw0Y7RidC40YUg0LjQvNC10L0KZGZfbWF0Y2ggPC0gZGF0YS5mcmFtZShuYW1lcyA9IGNvbW1vbl9uYW1lcywgdmFsdWVzLmluaXQgPSBjbnQuaW5pdFtjb21tb25fbmFtZXNdLCAKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMudG1wID0gY250LnRtcFtjb21tb25fbmFtZXNdKQoKCmdyYWRpZW50X2NvbG9ycyA8LSBjKGRpc2NyZXRlX3JhaW5ib3cobnJvdyhkZl9tYXRjaCkpKQpuYW1lcyhncmFkaWVudF9jb2xvcnMpID0gTlVMTAoKcCA9IGdncGxvdChkZl9tYXRjaCwgYWVzKHggPSB2YWx1ZXMuaW5pdCwgeSA9IHZhbHVlcy50bXAsIGxhYmVsID0gbmFtZXMsIGNvbG9yID0gbmFtZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICAjIGdlb21fdGV4dChoanVzdCA9IDAsIHZqdXN0ID0gMCkgKwogICMgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKG1heC5vdmVybGFwcyA9IDIwKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IHBhc3RlKG5hbWVzLCcoJyx2YWx1ZXMudG1wLCcpJyxzZXAgPScnKSksIG1heC5vdmVybGFwcyA9IDIwKSArCiAgeGxhYigiSW5pdGlhbCBjb3VudHMiKSArCiAgeWxhYigiY291bnRzIG9mIE5vIGhpdHMiKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBncmFkaWVudF9jb2xvcnMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgZ2VvbV90ZXh0KGFlcyh4PTAseT1JbmYsaGp1c3Q9MCwgdmp1c3Q9MywKICAgICAgICAgICAgICAgIGxhYmVsPXBhc3RlKCdMZW5ndGggPj0nLCBsZW4ubWluKSksIGNvbG9yID0gJ2dyZXkyMCcpCnAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAndGVzX3NlbGZfc2NhdHRlcl9ub19oaXRzX2xvbmcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCmBgYAoKYGBge3J9CgpoZWFkKGRmLnF1ZXJ5LnRtcFtkZi5xdWVyeS50bXAkZmFtaWx5ID09ICdETkEvTXVEUicsXSRxdWVyeSkKYGBgCgoKIyMjIEV4YW1wbGVzIG9uZSBzZWxmLWhpdAojIyMjIGZhbWlsaWVzCmBgYHtyfQoKCmRmLnF1ZXJ5LnRtcCA9IGRmLnF1ZXJ5WyhkZi5xdWVyeSR2YWwuaGl0cyA9PSAxKSxdCgpjbnQuaW5pdCA9IGModGFibGUoZGYucXVlcnkkZmFtaWx5KSkKY250LnRtcCA9IGModGFibGUoZGYucXVlcnkudG1wJGZhbWlseSkpCgpjb21tb25fbmFtZXMgPC0gaW50ZXJzZWN0KG5hbWVzKGNudC5pbml0KSwgbmFtZXMoY250LnRtcCkpCiMg0KHQvtC30LTQsNC90LjQtSBkYXRhZnJhbWUg0YLQvtC70YzQutC+INC00LvRjyDRgdC+0LLQv9Cw0LTQsNGO0YnQuNGFINC40LzQtdC9CmRmX21hdGNoIDwtIGRhdGEuZnJhbWUobmFtZXMgPSBjb21tb25fbmFtZXMsIHZhbHVlcy5pbml0ID0gY250LmluaXRbY29tbW9uX25hbWVzXSwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzLnRtcCA9IGNudC50bXBbY29tbW9uX25hbWVzXSkKCgpncmFkaWVudF9jb2xvcnMgPC0gYyhkaXNjcmV0ZV9yYWluYm93KG5yb3coZGZfbWF0Y2gpKSkKbmFtZXMoZ3JhZGllbnRfY29sb3JzKSA9IE5VTEwKCgpwID0gZ2dwbG90KGRmX21hdGNoLCBhZXMoeCA9IHZhbHVlcy5pbml0LCB5ID0gdmFsdWVzLnRtcCwgbGFiZWwgPSBuYW1lcywgY29sb3IgPSBuYW1lcykpICsKICBnZW9tX3BvaW50KCkgKwogICMgZ2VvbV90ZXh0KGhqdXN0ID0gMCwgdmp1c3QgPSAwKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKG1heC5vdmVybGFwcyA9IDIwKSArCiAgeGxhYigiSW5pdGlhbCBjb3VudHMiKSArCiAgeWxhYigiQ291bnRzIGluIFwiMSBzZWxmLWhpdHNcIiBjYXRlZ29yeSIpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGdyYWRpZW50X2NvbG9ycykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArCiAgdGhlbWVfbWluaW1hbCgpCnAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAndGVzX3NlbGZfc2NhdHRlcl8xX3NlbGZoaXRzX2ZhbS5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCmBgYAoKIyMjIyBzdWJmYW1pbGllcwpgYGB7cn0KCgpkZi5xdWVyeS50bXAgPSBkZi5xdWVyeVsoZGYucXVlcnkkdmFsLmhpdHMgPT0gMSkgJiAoZGYucXVlcnkkbGVuID49IDYwMCksXQoKY250LmluaXQgPSBjKHRhYmxlKGRmLnF1ZXJ5JHN1YmZhbSkpCmNudC50bXAgPSBjKHRhYmxlKGRmLnF1ZXJ5LnRtcCRzdWJmYW0pKQoKY29tbW9uX25hbWVzIDwtIGludGVyc2VjdChuYW1lcyhjbnQuaW5pdCksIG5hbWVzKGNudC50bXApKQojINCh0L7Qt9C00LDQvdC40LUgZGF0YWZyYW1lINGC0L7Qu9GM0LrQviDQtNC70Y8g0YHQvtCy0L/QsNC00LDRjtGJ0LjRhSDQuNC80LXQvQpkZl9tYXRjaCA8LSBkYXRhLmZyYW1lKG5hbWVzID0gY29tbW9uX25hbWVzLCB2YWx1ZXMuaW5pdCA9IGNudC5pbml0W2NvbW1vbl9uYW1lc10sIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcy50bXAgPSBjbnQudG1wW2NvbW1vbl9uYW1lc10pCgoKIyBncmFkaWVudF9jb2xvcnMgPC0gYyhkaXNjcmV0ZV9yYWluYm93KG5yb3coZGZfbWF0Y2gpKSkKbmFtZXMoZ3JhZGllbnRfY29sb3JzKSA9IE5VTEwKCgpwID0gZ2dwbG90KGRmX21hdGNoLCBhZXMoeCA9IHZhbHVlcy5pbml0LCB5ID0gdmFsdWVzLnRtcCwgbGFiZWwgPSBuYW1lcywgY29sb3IgPSBuYW1lcykpICsKICBnZW9tX3BvaW50KCkgKwogICMgZ2VvbV90ZXh0KGhqdXN0ID0gMCwgdmp1c3QgPSAwKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKG1heC5vdmVybGFwcyA9IDIwKSArCiAgeGxhYigiSW5pdGlhbCBjb3VudHMiKSArCiAgeWxhYigiQ291bnRzIGluIFwiMSBzZWxmLWhpdHNcIiBjYXRlZ29yeSIpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgIyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZ3JhZGllbnRfY29sb3JzKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICB0aGVtZV9taW5pbWFsKCkKcAoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICd0ZXNfc2VsZl9zY2F0dGVyXzFfc2VsZmhpdHNfc3ViZmFtLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA3LCBoZWlnaHQgPSA1KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgpgYGAKCiMjIyMgaW5kaXZpZHVhbHMgZnJvbSBzdWJmYW1pbGllcwpgYGB7cn0Kcy5zdWJmYW0gPSAnQVRSRVA4JwoKZGYucXVlcnkudG1wID0gZGYucXVlcnlbKGRmLnF1ZXJ5JHN1YmZhbSA9PSBzLnN1YmZhbSkgJiAoZGYucXVlcnkkbGVuID49IDYwMCksXQpkZi5xdWVyeS50bXAKCgpgYGAKCgoKCgojIyBDcmVhdGluZyB0aGUgZ3JhcGgKYGBge3J9CiMgYWxsIGVkZ2VzCmlkeCA9IHJlcy5uZXN0JHAxID49IHNpbS5jdXRvZmYKZWRnZXMgPSBjYmluZChyZXMubmVzdCRWMVtpZHhdLCByZXMubmVzdCRWOFtpZHhdKQppZHggPSByZXMubmVzdCRwOCA+PSBzaW0uY3V0b2ZmCmVkZ2VzID0gcmJpbmQoZWRnZXMsIGNiaW5kKHJlcy5uZXN0JFY4W2lkeF0sIHJlcy5uZXN0JFYxW2lkeF0pKQp0ZS5lbmdlcy5uYW1lcyA9IHVuaXF1ZShjKGVkZ2VzWywxXSwgZWRnZXNbLDJdKSkKdGUuZW5nZXMuZmFtID0gc2FwcGx5KHRlLmVuZ2VzLm5hbWVzLCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs5XSApCgp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnRE5BL1BvZ28nLCAnRE5BL1RjMScsICdETkEvSGFyYmluZ2VyJywgJ0ROQS9Fbi1TcG0nLAogICAgICAgICAgICAgICAgICAgICAnRE5BL0hBVCcsICdETkEnLCAnRE5BL01hcmluZXInKV0gPSAnRE5BJwp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnUmF0aEUxX2NvbnMnLCAnUmF0aEUyX2NvbnMnLCAnUmF0aEUzX2NvbnMnKV0gPSAnUmF0aEUxLzIvM19jb25zJwp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnTElORS9MMScsICdMSU5FPycpXSA9ICdMSU5FJwp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnVW5hc3NpZ25lZCcpXSA9ICdNaXgnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdSQy9IZWxpdHJvbicpXSA9ICdIZWxpdHJvbicKCmVkZ2VzID0gZWRnZXNbdGUuZW5nZXMuZmFtW2VkZ2VzWywxXV0gIT0gJ1RFRycsXQplZGdlcyA9IGVkZ2VzW3RlLmVuZ2VzLmZhbVtlZGdlc1ssMl1dICE9ICdURUcnLF0KdGUuZW5nZXMubmFtZXMgPSB1bmlxdWUoYyhlZGdlc1ssMV0sIGVkZ2VzWywyXSkpCgoKIyBub2RlcwppZHggPSAocmVzLm5lc3QkcDEgPj0gc2ltLmN1dG9mZikgJiAocmVzLm5lc3QkcDggPj0gc2ltLmN1dG9mZikKdGUubm9kZXMgPSBjYmluZChyZXMubmVzdCRWMVtpZHhdLCByZXMubmVzdCRWOFtpZHhdKQp0ZS5ub2RlcyA9IHRlLm5vZGVzW3RlLmVuZ2VzLmZhbVt0ZS5ub2Rlc1ssMV1dICE9ICdURUcnLF0KdGUubm9kZXMgPSB0ZS5ub2Rlc1t0ZS5lbmdlcy5mYW1bdGUubm9kZXNbLDJdXSAhPSAnVEVHJyxdCgp0ZS5yZXN0ID0gc2V0ZGlmZih0ZS5lbmdlcy5uYW1lcywgYyh0ZS5ub2Rlc1ssMV0sIHRlLm5vZGVzWywyXSkpCgoKdGUubm9kZXMuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQodGUubm9kZXMpLCBkaXJlY3RlZCA9IFQpCnRlLm5vZGVzLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodGUubm9kZXMuZ3JhcGgpCnRlLm5vZGVzLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLm5vZGVzLmdyYXBoKQoKbm9kZXMgPSBkYXRhLmZyYW1lKG5vZGUgPSBwYXN0ZSgnTicsIHRlLm5vZGVzLmNvbXAkbWVtYmVyc2hpcCwgc2VwID0gJycpLCAKICAgICAgICAgICAgICAgICAgIHRlID0gbmFtZXModGUubm9kZXMuY29tcCRtZW1iZXJzaGlwKSkKCm5vZGVzLnJlc3QgPSBkYXRhLmZyYW1lKG5vZGUgPSBwYXN0ZSgnUicsICgxOmxlbmd0aCh0ZS5yZXN0KSksIHNlcCA9ICcnKSwgdGUgPSB0ZS5yZXN0KQpub2RlcyA9IHJiaW5kKG5vZGVzLCBub2Rlcy5yZXN0KQoKcm93bmFtZXMobm9kZXMpID0gbm9kZXMkdGUKCgpub2Rlcy5jbnQgPSBkYXRhLmZyYW1lKGNudCA9IGModGFibGUobm9kZXMkbm9kZSkpKQpub2Rlcy5jbnQkbm9kZSA9IHJvd25hbWVzKG5vZGVzLmNudCkKbm9kZXMuY250JGZhbSA9IHNhcHBseShub2Rlcy5jbnQkbm9kZSwgZnVuY3Rpb24ocyl7CiAgcy50ZSA9IG5vZGVzJHRlW25vZGVzJG5vZGUgPT0gc10KICBmYW0udGUgPSB1bmlxdWUodGUuZW5nZXMuZmFtW3MudGVdKQogIGlmKGxlbmd0aChmYW0udGUpID09IDEpewogICAgcmV0dXJuKGZhbS50ZSkKICB9IGVsc2UgewogICAgZmFtLnRlID0gc2V0ZGlmZihmYW0udGUsICdURUcnKQogICAgaWYobGVuZ3RoKGZhbS50ZSkgPT0gMSkgcmV0dXJuKGZhbS50ZSkKICAgIHJldHVybignTWl4JykKICB9Cn0pCnRhYmxlKG5vZGVzLmNudCRmYW0pCgoKIyBSZWRlZmluZSBlZGdlcyBidXQgd2l0aCBub2RlIG5hbWVzCmlkeC5lbmRlcyA9IChlZGdlc1ssMV0gJWluJSBub2RlcyR0ZSkgJiAoZWRnZXNbLDJdICVpbiUgbm9kZXMkdGUpCmIuZ3JhcGggPSBjYmluZChub2Rlc1tlZGdlc1tpZHguZW5kZXMsMV0sICdub2RlJ10sbm9kZXNbZWRnZXNbaWR4LmVuZGVzLDJdLCAnbm9kZSddKQpiLmdyYXBoID0gdW5pcXVlKGIuZ3JhcGgpCiMgYi5ncmFwaCA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gIT0gYi5ncmFwaFssMl0sXQpiLmdyYXBoLnVuaSA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gPT0gYi5ncmFwaFssMl0sXQpiLmdyYXBoID0gYi5ncmFwaFtiLmdyYXBoWywxXSAhPSBiLmdyYXBoWywyXSxdCgpsZW5ndGgodW5pcXVlKGMoYi5ncmFwaFssMV0sIGIuZ3JhcGhbLDJdKSkpCgojIHJlZHVjZSBpbmRpcmVjdCBhcnJvd3MKaWR4LnJlbW92ZSA9IGMoKQpmb3IoaS5lZGdlIGluIDE6bnJvdyhiLmdyYXBoKSl7CiAgaWYoaS5lZGdlICUlIDEwMDAgPT0gMCkgcHJpbnQoaS5lZGdlKQogIHRtcC50byA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gPT0gYi5ncmFwaFtpLmVkZ2UsMV0sMl0KICB0bXAuZnJvbSA9IGIuZ3JhcGhbYi5ncmFwaFssMl0gPT0gYi5ncmFwaFtpLmVkZ2UsMl0sMV0KICBpZihsZW5ndGgoaW50ZXJzZWN0KHRtcC50bywgdG1wLmZyb20pKSA+IDApIGlkeC5yZW1vdmUgPSBjKGlkeC5yZW1vdmUsIGkuZWRnZSkKfQppZHgucmVtb3ZlID0gdW5pcXVlKGlkeC5yZW1vdmUpCmIuZ3JhcGggPSBiLmdyYXBoWy1pZHgucmVtb3ZlLF0KIyBiLmdyYXBoID0gcmJpbmQoYi5ncmFwaCwgYi5ncmFwaC51bmkpCgojIFByaW50IGdyYXBoCgpnLm5vZGVzLmZhbSA9IG5vZGVzLmNudCRmYW0KbmFtZXMoZy5ub2Rlcy5mYW0pID0gbm9kZXMuY250JG5vZGUKZy5ub2Rlcy5jbnQgPSBub2Rlcy5jbnQkY250Cm5hbWVzKGcubm9kZXMuY250KSA9IG5vZGVzLmNudCRub2RlCgpnLmNvbHMgPSBkaXNjcmV0ZV9yYWluYm93KGxlbmd0aCh1bmlxdWUoZy5ub2Rlcy5mYW0pKSkKbmFtZXMoZy5jb2xzKSA9IHVuaXF1ZShnLm5vZGVzLmZhbSkKCmIuZ3JhcGguaW5pdCA9IGIuZ3JhcGgKCgpnLnBhcnQgPC0gbmV0d29yayhiLmdyYXBoLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKYi5ncmFwaC5uYW1lcyA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydCkKYGBgCgojIyMgT2xkIGNvbG9ycwpgYGB7cn0KIyBwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAojICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKIyAgICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXNdLAojICAgICAgICAgICAgIHBhbGV0dGUgPSBnLmNvbHMsCiMgICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgojICAgICAgICAgICAgICkgCiMgcCArIGd1aWRlcyhzaXplID0gRikKIyAKIyAjIAojICMgYi5ncmFwaC5mYW0gPSBjYmluZChnLm5vZGVzLmZhbVtiLmdyYXBoWywxXV0sIGcubm9kZXMuZmFtW2IuZ3JhcGhbLDJdXSkKIyAjIGIuZ3JhcGguZmFtCiMgIyAKIyAjIHdoaWNoKChiLmdyYXBoLmZhbVssMV0gPT0gJ0ROQS9NdURSJykgJiAoYi5ncmFwaC5mYW1bLDFdID09ICdMSU5FJykpCiMgCgoKYGBgCgojIyMgTmV3IEZhbWlseSBjb2xvcnMKYGBge3J9CmcuZmFtLm5hbWVzID0gc29ydCh1bmlxdWUoZy5ub2Rlcy5mYW0pKQpmYW0ucGFsZXR0ZSA9IGMoKQppZHgucGFsbGV0ZSA9IGMoKQoKaWR4LmZhbSA8LSBncmVwKCJeSGVsaXRyb24iLCBnLmZhbS5uYW1lcywgdmFsdWUgPSBGQUxTRSkKdG1wLnBhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCcjQkZBQ0UyJywgJyMyNjZEOTgnLCAnIzQyMkI3MicpKShsZW5ndGgoaWR4LmZhbSkpCmlkeC5wYWxsZXRlID0gYyhpZHgucGFsbGV0ZSwgaWR4LmZhbSkKZmFtLnBhbGV0dGUgPSBjKGZhbS5wYWxldHRlLCB0bXAucGFsZXR0ZSkKCmlkeC5mYW0gPC0gZ3JlcCgiXkxUUiIsIGcuZmFtLm5hbWVzLCB2YWx1ZSA9IEZBTFNFKQp0bXAucGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoJyNCRkRCMzgnLCAnIzU0QjQzNScpKShsZW5ndGgoaWR4LmZhbSkpCmlkeC5wYWxsZXRlID0gYyhpZHgucGFsbGV0ZSwgaWR4LmZhbSkKZmFtLnBhbGV0dGUgPSBjKGZhbS5wYWxldHRlLCB0bXAucGFsZXR0ZSkKCmlkeC5mYW0gPC0gZ3JlcCgiXkROQSIsIGcuZmFtLm5hbWVzLCB2YWx1ZSA9IEZBTFNFKQp0bXAucGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoJyNGOUI1RDAnLCAnIzk3MTU0OScpKShsZW5ndGgoaWR4LmZhbSkpCmlkeC5wYWxsZXRlID0gYyhpZHgucGFsbGV0ZSwgaWR4LmZhbSkKZmFtLnBhbGV0dGUgPSBjKGZhbS5wYWxldHRlLCB0bXAucGFsZXR0ZSkKCmlkeC5mYW0gPSBzZXRkaWZmKDE6bGVuZ3RoKGcuZmFtLm5hbWVzKSwgaWR4LnBhbGxldGUpCnRtcC5wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygnI0ZGQzI2RicsICcjQzM4MTU0JywgJyM4ODRBMzknLCAnIzRFMzYzNicpKShsZW5ndGgoaWR4LmZhbSkpCmlkeC5wYWxsZXRlID0gYyhpZHgucGFsbGV0ZSwgaWR4LmZhbSkKZmFtLnBhbGV0dGUgPSBjKGZhbS5wYWxldHRlLCB0bXAucGFsZXR0ZSkKCm5hbWVzKGZhbS5wYWxldHRlKSA9IGcuZmFtLm5hbWVzW2lkeC5wYWxsZXRlXQpmYW0ucGFsZXR0ZVsnVW5hc3NpZ25lZCddID0gJ2dyZXknCmZhbS5wYWxldHRlWydNaXgnXSA9ICdibGFjaycKZmFtLnBhbGV0dGVbJ1RFRyddID0gJ2RhcmtncmVlbicKCgoKCmBgYAoKYGBge3J9CgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUsCiAgICAgICAgICAgICMgbW9kZSA9ICJrYW1hZGFrYXdhaSIKICAgICAgICAgICAgKSAKcCA9IHAgKyBndWlkZXMoc2l6ZSA9IEYpIApwID0gcCArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkKCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3Rlc19mYW1pbHkucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3Rlc19mYW1pbHlfbGVnZW5kLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA3LCBoZWlnaHQgPSA1KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKCgoKIyMgU2VwYXJhdGVseSB2aXN1YWxpc2UgY29ubmVjdGVkIGNvbXBvbmVudHMKYGBge3J9CnRtcC5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChiLmdyYXBoKSwgZGlyZWN0ZWQgPSBUKQp0bXAuZ3JhcGggPC0gaWdyYXBoOjpzaW1wbGlmeSh0bXAuZ3JhcGgpCnRtcC5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0bXAuZ3JhcGgpCgp0bXAuY250ID0gdGFibGUodG1wLmNvbXAkbWVtYmVyc2hpcCkKdG1wLmNudCA9IC1zb3J0KC10bXAuY250KQpoZWFkKHRtcC5jbnQpCgprID0gMQp0bXAuayA9IGFzLm51bWVyaWMobmFtZXModG1wLmNudClba10pCnRtcC5uYW1lcyA9IG5hbWVzKHRtcC5jb21wJG1lbWJlcnNoaXApW3RtcC5jb21wJG1lbWJlcnNoaXAgPT0gdG1wLmtdCmIuZ3JhcGguc3ViID0gYi5ncmFwaFsoYi5ncmFwaFssMV0gJWluJSB0bXAubmFtZXMpICYgCiAgICAgICAgICAgICAgICAgICAgICAgIChiLmdyYXBoWywyXSAlaW4lIHRtcC5uYW1lcyksXQoKZy5wYXJ0LnN1Yi5iaWcgPC0gbmV0d29yayhiLmdyYXBoLnN1YiwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMuc3ViLmJpZyA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydC5zdWIuYmlnKQoKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLmJpZywgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiAgICAgICAgICAgIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKQpwLmJpZy50eXBlID0gcCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgc2V0LnNlZWQoMjApCiMgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5iaWcsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAojICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sIAojICAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwKIyAgICAgICAgICAgICBtb2RlID0gJ2thbWFkYWthd2FpJywKIyAgICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKQojIHAuYmlnLmNvbG9yID0gcCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgp0bXAuayA9IGFzLm51bWVyaWMobmFtZXModG1wLmNudClba10pCnRtcC5uYW1lcyA9IG5hbWVzKHRtcC5jb21wJG1lbWJlcnNoaXApW3RtcC5jb21wJG1lbWJlcnNoaXAgIT0gdG1wLmtdCmIuZ3JhcGguc3ViID0gYi5ncmFwaFsoYi5ncmFwaFssMV0gJWluJSB0bXAubmFtZXMpICYgCiAgICAgICAgICAgICAgICAgICAgICAgIChiLmdyYXBoWywyXSAlaW4lIHRtcC5uYW1lcyksXQoKZy5wYXJ0LnN1Yi5zbWFsbCA8LSBuZXR3b3JrKGIuZ3JhcGguc3ViLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKYi5ncmFwaC5uYW1lcy5zdWIuc21hbGwgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQuc3ViLnNtYWxsKQoKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLnNtYWxsLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sCiAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCnAuc21hbGwudHlwZSA9cCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgc2V0LnNlZWQoMjApCiMgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5zbWFsbCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiMgICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLCAKIyAgICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwKIyAgICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAojICAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCiMgcC5zbWFsbC5jb2xvciA9IHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKCmBgYAojIyMgUGxvdHMKYGBge3J9CnAuYmlnLnR5cGUKcC5zbWFsbC50eXBlCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3Rlc19mYW1pbHlfc21hbGwucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCnByaW50KHAuc21hbGwudHlwZSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfdGVzX2ZhbWlseV9iaWcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHAuYmlnLnR5cGUpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgojIFN0b3AgZm9yIHRoZSBwYXBlcgpgYGB7cn0Kc3RvcCgpCmBgYAoKCiMjIFNwZWNpZmljIFRFIGZhbWlsaWVzCiMjIyBHcmFwaCBvZiBvbmUgZmFtaWx5CgpgYGB7cn0Kc29ydCgtdGFibGUoZGYucXVlcnkkc3ViZmFtWyhkZi5xdWVyeSR2YWwuaGl0cyA9PSAzKSAmIChkZi5xdWVyeSRmYW1pbHkgPT0gJ0xUUi9Db3BpYScpXSkpCmBgYAoKCgpgYGB7cn0KCiMgb25lLnRlLmZhbSA9ICdCUk9EWUFHQTEnCiMgb25lLnRlLmZhbSA9ICdCUk9EWUFHQTInCiMgb25lLnRlLmZhbSA9ICdIRUxJVFJPTlkxRCcKIyBvbmUudGUuZmFtID0gJ0hFTElUUk9OWTMnCm9uZS50ZS5mYW0gPSAnQVRDT1BJQTQxJwpxdWVyeS5mYW0gPSBkZi5xdWVyeSRxdWVyeVtkZi5xdWVyeSRzdWJmYW0gPT0gb25lLnRlLmZhbV0KCgpvbmUudGUuZmFtID0gJ0FUQ09QSUE0MScKcXVlcnkuZmFtID0gZGYucXVlcnkkcXVlcnlbZGYucXVlcnkkc3ViZmFtID09IG9uZS50ZS5mYW1dCgpyZXMubmVzdC5mYW1wID0gcmVzLm5lc3RbKHJlcy5uZXN0JFYxICVpbiUgcXVlcnkuZmFtKSB8IChyZXMubmVzdCRWOCAlaW4lIHF1ZXJ5LmZhbSksXQoKCmlkeCA9IHJlcy5uZXN0LmZhbXAkcDEgPj0gc2ltLmN1dG9mZgplZGdlcyA9IGNiaW5kKHJlcy5uZXN0LmZhbXAkVjFbaWR4XSwgcmVzLm5lc3QuZmFtcCRWOFtpZHhdKQppZHggPSByZXMubmVzdC5mYW1wJHA4ID49IHNpbS5jdXRvZmYKZWRnZXMgPSByYmluZChlZGdlcywgY2JpbmQocmVzLm5lc3QuZmFtcCRWOFtpZHhdLCByZXMubmVzdC5mYW1wJFYxW2lkeF0pKQoKCnRlLmVuZ2VzLm5hbWVzID0gdW5pcXVlKGMoZWRnZXNbLDFdLCBlZGdlc1ssMl0pKQp0ZS5lbmdlcy5mYW0gPSBzYXBwbHkodGUuZW5nZXMubmFtZXMsIGZ1bmN0aW9uKHMpIHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzldICkKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ0ROQS9Qb2dvJywgJ0ROQS9UYzEnLCAnRE5BL0hhcmJpbmdlcicsICdETkEvRW4tU3BtJywKICAgICAgICAgICAgICAgICAgICAgJ0ROQS9IQVQnLCAnRE5BJywgJ0ROQS9NYXJpbmVyJyldID0gJ0ROQScKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ1JhdGhFMV9jb25zJywgJ1JhdGhFMl9jb25zJywgJ1JhdGhFM19jb25zJyldID0gJ1JhdGhFMS8yLzNfY29ucycKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ0xJTkUvTDEnLCAnTElORT8nKV0gPSAnTElORScKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ1VuYXNzaWduZWQnKV0gPSAnTWl4Jwp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnUkMvSGVsaXRyb24nKV0gPSAnSGVsaXRyb24nCgpnLnBhcnQgPC0gbmV0d29yayhlZGdlcywgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQpCmIuZ3JhcGgubGVuID0gYXMubnVtZXJpYyhzYXBwbHkoc3Ryc3BsaXQoYi5ncmFwaC5uYW1lcywgIlxcfCIpLCBmdW5jdGlvbih4KSB4WzVdKSkKCgpsYWJlbC5mYW1pbHkgPSBzYXBwbHkoc3Ryc3BsaXQoYi5ncmFwaC5uYW1lcywgIlxcfCIpLCBmdW5jdGlvbih4KSB4WzhdKQpsYWIuY29scyA9IGMoJyMzRjJFM0UnLCAid2hpdGUiKQpsYWJlbC5jb2xvciA9IGxhYi5jb2xzWyhsYWJlbC5mYW1pbHkgPT0gb25lLnRlLmZhbSkgKyAxXQoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBiLmdyYXBoLmxlbiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgbm9kZS5zaXplID0gMTUsCiAgICAgICAgICAgIGFscGhhPTAuOCwKICAgICAgICAgICAgYXJyb3cuZ2FwID0gMC4wMTUsCiAgICAgICAgICAgIGFycm93LnNpemUgPSA1LAogICAgICAgICAgICBsYWJlbC5jb2xvciA9IGxhYmVsLmNvbG9yLAogICAgICAgICAgICAjIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAgICAgICAgICAgY29sb3IgPSB0ZS5lbmdlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSwKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKQpwIAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3JlYWxfdGVzX3N1YmZhbV8nLCBvbmUudGUuZmFtLCAnLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSAyMCwgaGVpZ2h0ID0gMTgpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBiLmdyYXBoLm5hbWVzLCBlZGdlLmNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDE1LAogICAgICAgICAgICBhbHBoYT0wLjgsCiAgICAgICAgICAgIGFycm93LmdhcCA9IDAuMDE1LAogICAgICAgICAgICBhcnJvdy5zaXplID0gNSwKICAgICAgICAgICAgIyBsYWJlbC5jb2xvciA9IGxhYmVsLmNvbG9yLAogICAgICAgICAgICAjIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBjb2xvciA9IHRlLmVuZ2VzLmZhbVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAncmVhbF90ZXNfc3ViZmFtXycsIG9uZS50ZS5mYW0sICdfbmFtZXMucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUwLCBoZWlnaHQgPSA0OSkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpgYGAKCgojIyBEb3RwbG90cwojIyMjIEZ1bmN0aW9ucwpgYGB7cn0Kc2VxMm14IDwtIGZ1bmN0aW9uKHNlcSwgd3NpemUpewogIAogIG51bV9yb3dzIDwtIGxlbmd0aChzZXEpIC0gd3NpemUgKyAxCiAgbWF0cml4X3NlcSA8LSBtYXRyaXgobnJvdyA9IG51bV9yb3dzLCBuY29sID0gd3NpemUpCiAgZm9yIChpIGluIDE6bnVtX3Jvd3MpIHsKICAgIG1hdHJpeF9zZXFbaSwgXSA8LSBzZXFbaTooaSArIHdzaXplIC0gMSldCiAgfQoKICByZXR1cm4obWF0cml4X3NlcSkKfQoKbXhDb21wIDwtIGZ1bmN0aW9uKG14MSwgbXgyLCB3c2l6ZSwgbm1hdGNoKXsKICBteC5yZXMgPSAwCiAgZm9yKHMgaW4gYygnQScsICdDJywgJ0cnLCAnVCcpKXsKICAgIG14LnJlcyA9IG14LnJlcyArIChteDEgPT0gcykgJSolIHQobXgyID09IHMpCiAgfQogICMgbXgucmVzID0gKG14LnJlcyA+PSBubWF0Y2gpICogMQogIG14LnJlc1tteC5yZXMgPCBubWF0Y2hdID0gMAogIAogIGluZGljZXMgPC0gd2hpY2gobXgucmVzICE9IDAsIGFyci5pbmQgPSBUUlVFKQogIHZhbHVlcyA8LSBteC5yZXNbaW5kaWNlc10KICByZXN1bHQgPC0gY2JpbmQoaW5kaWNlcywgdmFsdWVzKQogIHJlc3VsdCA9IGFzLmRhdGEuZnJhbWUocmVzdWx0KQogIHJldHVybihyZXN1bHQpCn0KCmRvdHBsb3QgPC0gZnVuY3Rpb24oc2VxMSwgc2VxMiwgd3NpemUsIG5tYXRjaCkgewogIHNlcTIucmMgPSByZXYoc2VxaW5yOjpjb21wKHNlcTIpKQoKICBteDEgPSB0b3VwcGVyKHNlcTJteChzZXExLCB3c2l6ZSkpCiAgbXgyID0gdG91cHBlcihzZXEybXgoc2VxMiwgd3NpemUpKQogIAogIHJlc3VsdCA9IG14Q29tcChteDEsIG14Miwgd3NpemUsIG5tYXRjaCkKICAKICBteDIucmMgPSB0b3VwcGVyKHNlcTJteChzZXEyLnJjLCB3c2l6ZSkpCiAgcmVzdWx0LnJjID0gbXhDb21wKG14MSwgbXgyLnJjLCB3c2l6ZSwgbm1hdGNoKQogIHJlc3VsdC5yYyR2YWx1ZXMgPSAtcmVzdWx0LnJjJHZhbHVlcwogIHJlc3VsdC5yYyRjb2wgPSBsZW5ndGgoc2VxMikgLSByZXN1bHQucmMkY29sIC0gd3NpemUgKyAyCiAgcmVzdWx0ID0gcmJpbmQocmVzdWx0LnJjLCByZXN1bHQpCiAgCiAgCiAgcCA9IGdncGxvdChyZXN1bHQsIGFlcyh4ID0gcm93LCB5ID0gY29sLCBmaWxsID0gdmFsdWVzKSkgKwogICAgZ2VvbV90aWxlKHdpZHRoID0gMSwgaGVpZ2h0ID0gMSkgKwogICAgIyB4bGFiKG5hbWUxKSArIHlsYWIobmFtZTIpICsKICAgICMgeGxhYihwYXN0ZTAoc3Ryc3BsaXQobmFtZTEsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ3wnKSkgKyAKICAgICMgeWxhYihwYXN0ZTAoc3Ryc3BsaXQobmFtZTIsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ3wnKSkgKwogICAgIyB4bGFiKCcnKSArIHlsYWIoJycpICsKICAgIGd1aWRlcyhmaWxsID0gRkFMU0UpICsKICAgIHRoZW1lX21pbmltYWwoKSArIGNvb3JkX2ZpeGVkKCkgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsgCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gIiNDRTFGNkEiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gIiMyNzM3NEQiLCBtaWRwb2ludCA9IDApICsKICAgIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MSkpCiAgcCAKICByZXR1cm4ocCkKfQoKCnNlcUNvbXBsZXhpdHkgPC0gZnVuY3Rpb24oc2VxMSwgbWV0aG9kPSdkb3RwbG90Jywgd3NpemU9MTAsIG5tYXRjaD04KSB7CgogIG14MSA9IHRvdXBwZXIoc2VxMm14KHNlcTEsIHdzaXplKSkKICByZXN1bHQgPSBteENvbXAobXgxLCBteDEsIHdzaXplLCBubWF0Y2gpCiAgCiAgc2VxMS5yYyA9IHJldihzZXFpbnI6OmNvbXAoc2VxMSkpCiAgbXgxLnJjID0gdG91cHBlcihzZXEybXgoc2VxMS5yYywgd3NpemUpKQogIHJlc3VsdC5yYyA9IG14Q29tcChteDEsIG14MS5yYywgd3NpemUsIG5tYXRjaCkKICAKICBuLm1hdGNoID0gKG5yb3cocmVzdWx0KSArIG5yb3cocmVzdWx0LnJjKSkgLyBsZW5ndGgoc2VxMSkgCiAgCiAgcmV0dXJuKHApCn0KCmBgYAoKCmBgYHtyfQoKcCA9IGRvdHBsb3Qoc2VxMSwgc2VxMSwgd3NpemUsIG5tYXRjaCkKcApgYGAKCgojIyMgUmVhZCBURSBzZXF1ZW5jZXMKYGBge3J9CmZpbGUudGUuZmFzdGEgPSBwYXN0ZShwYXRoLnRhaXIsICduZXdfdGUuZmFzdGEnLCBzZXAgPSAnJykKdGUuZmFzdGEgPSBzZXFpbnI6OnJlYWQuZmFzdGEoZmlsZS50ZS5mYXN0YSkKdGUubmFtZXMgPSBuYW1lcyh0ZS5mYXN0YSkKdGUuZmFzdGEgPSBzZXFpbnI6OmdldFNlcXVlbmNlKHRlLmZhc3RhKQpuYW1lcyh0ZS5mYXN0YSkgPSB0ZS5uYW1lcwpgYGAKCiMjIyBPbmUgcGFpcndpc2UgZXhhbXBsZQpgYGB7cn0Kd3NpemUgPSAxMApubWF0Y2ggPSA4CgpzZXExID0gdGUuZmFzdGFbW2IuZ3JhcGgubmFtZXNbMV1dXQpzZXEyID0gdGUuZmFzdGFbW2IuZ3JhcGgubmFtZXNbMV1dXQoKCm5hbWUxID0gJ3RlfDEyMzg0NzYzfDEyMzg1MjYyfDR8NTAwfCt8QVQ0VEU1NzU4MHxCUk9EWUFHQTFBfEROQS9NdURSJwpuYW1lMiA9ICd0ZXwxMzY3NDkxN3wxMzY3NTI3MXwxfDM1NXwrfEFUMVRFNDQ3NjB8QlJPRFlBR0ExfEROQS9NdURSJwoKIyBuYW1lMSA9ICd0ZXwxMDU5MjExMXwxMDU5MjY2NHwxfDU1NHwtfEFUMVRFMzQyNjV8QlJPRFlBR0EyfEROQS9NdURSJwojIG5hbWUyID0gJ3RlfDg3NDMyMzh8ODc0NDI2M3w0fDEwMjZ8LXxBVDRURTM5MDQ1fEhFTElUUk9OWTFEfFJDL0hlbGl0cm9uJwoKbmFtZTEgPSAndGV8NjI4MzE5OHw2Mjg0NDIxfDR8MTIyNHwtfEFUNFRFMjY3MTB8QVRSRVAxNXxSQy9IZWxpdHJvbicKbmFtZTIgPSAndGV8NjI4MzE5OHw2Mjg0NDIxfDR8MTIyNHwtfEFUNFRFMjY3MTB8QVRSRVAxNXxSQy9IZWxpdHJvbicKCnNlcTEgPSB0ZS5mYXN0YVtbbmFtZTFdXQpzZXEyID0gdGUuZmFzdGFbW25hbWUyXV0KCnAgPSBkb3RwbG90KHNlcTEsIHNlcTIsIHdzaXplLCBubWF0Y2gpCgpwID0gcCArIGFubm90YXRlKCJ0ZXh0IiwgeCA9IC1JbmYsIHkgPSBJbmYsIGxhYmVsID0gcGFzdGUoJ3dzaXplPScsd3NpemUsJ1xubm1hdGNoPScsbm1hdGNoLCBzZXAgPSAnJyksIAogICAgICAgICAgICAgaGp1c3QgPSAtMC4xLCB2anVzdCA9IDEuMSkKCnAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdwYWlyd2lzZV8nLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCmBgYAoKIyMjIG9uZSBWUyBhbGwKYGBge3J9Cgp3c2l6ZSA9IDEwCm5tYXRjaCA9IDgKCm5hbWUwID0gJ3RlfDExNjgzNTY1fDExNjg5ODIxfDN8NjI1N3wrfEFUM1RFNDg1NDB8QVRDT1BJQTk1fExUUi9Db3BpYScKbmFtZTAgPSAndGV8MTY2OTE3NDh8MTY2OTUxNTR8MXwzNDA3fOKIknxBVDFURTU1MDcwfEFUQ09QSUE0MXxMVFIvQ29waWEnCm5hbWUwID0gZ3N1Yign4oiSJywgIi0iLCBuYW1lMCkKCgpvbmUudGUuZmFtID0gc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzhdCiMgb25lLnRlLmZhbSA9ICdCUk9EWUFHQTInCnF1ZXJ5LmZhbSA9IGRmLnF1ZXJ5JHF1ZXJ5W2RmLnF1ZXJ5JHN1YmZhbSA9PSBvbmUudGUuZmFtXQpxdWVyeS5mYW0gPSBxdWVyeS5mYW1bKHF1ZXJ5LmZhbSAlaW4lIHJlcy5uZXN0LnNpbSRWMSkgfCAocXVlcnkuZmFtICVpbiUgcmVzLm5lc3Quc2ltJFYyKV0KCm5hbWVzLmFsbCA9IHNldGRpZmYocXVlcnkuZmFtLCBuYW1lMCkKCnAuYWxsID0gbGlzdCgpCmZvcihuYW1lMiBpbiBuYW1lcy5hbGwpewogICMgbWVzc2FnZShuYW1lMikKICBzZXExID0gdGUuZmFzdGFbW25hbWUwXV0KICBzZXEyID0gdGUuZmFzdGFbW25hbWUyXV0KICAKICBzMSA9IHN0cnNwbGl0KG5hbWUwLCAnXFx8JylbWzFdXVs3XQogIHMyID0gc3Ryc3BsaXQobmFtZTIsICdcXHwnKVtbMV1dWzddCiAgcCA9IGRvdHBsb3Qoc2VxMSwgc2VxMiwgd3NpemUsIG5tYXRjaCkgKyB4bGFiKHMxKSArIHlsYWIoczIpCiAgcC5hbGxbW25hbWUyXV0gPSBwCn0KIyAKIyBwcCA9IGdyaWQuYXJyYW5nZShncm9icyA9IHAuYWxsLCBuY29sID0gMTMpICMjIGRpc3BsYXkgcGxvdAojIAojIAojIHBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdwYWlyd2lzZV9hbGwnLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUwLCBoZWlnaHQgPSA1MCkKIyBwcmludChwcCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKIyBkZXYub2ZmKCkKCnMwID0gcGFzdGUwKHN0cnNwbGl0KG5hbWUwLCAnXFx8JylbWzFdXVs3OjldLCBjb2xsYXBzZSA9ICdfJykKczAgPSBnc3ViKCIvIiwgIi0iLCBzMCkKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3BhaXJ3aXNlX2FsbF8nLHMwLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUwLCBoZWlnaHQgPSA1MCkKZ3JpZC5hcnJhbmdlKGdyb2JzID0gcC5hbGwsIG5jb2wgPSBjZWlsaW5nKHNxcnQobGVuZ3RoKHAuYWxsKSkpKSAjIFdyaXRlIHRoZSBncmlkLmFycmFuZ2UgaW4gdGhlIGZpbGUKZGV2Lm9mZigpICMgQ2xvc2UgdGhlIGZpbGUKCmBgYAoKCiMjIyBvbmUgY29ubmVjdGVkIGNvbXBvbmVudApgYGB7cn0KCndzaXplID0gMTAKbm1hdGNoID0gOAoKCm5hbWUwID0gJ3RlfDYyMDU2MjF8NjIwNjE4NHwyfDU2NHziiJJ8QVQyVEUyNTI1NXxIRUxJVFJPTlkxRHxSQy9IZWxpdHJvbicKbmFtZTAgPSAndGV8MTQxODkyNTZ8MTQxOTAyNjZ8NXwxMDExfC18QVQ1VEU1MDcwMHxIRUxJVFJPTlkzfFJDL0hlbGl0cm9uJwpuYW1lMCA9ICd0ZXwxMjUxMzIzOXwxMjUxMzgyNHwxfDU4NnwrfEFUMVRFNDA3MjV8QVRISUxBNEF8TFRSL0d5cHN5JwpuYW1lMCA9ICd0ZXwxMTY0NzQyNnwxMTY0ODkxMnwxfDE0ODd8K3xBVDFURTM3NzA1fEFUUkVQN3xSQy9IZWxpdHJvbicKbmFtZTAgPSAndGV8MTE2ODM1NjV8MTE2ODk4MjF8M3w2MjU3fCt8QVQzVEU0ODU0MHxBVENPUElBOTV8TFRSL0NvcGlhJwpuYW1lMCA9IGdzdWIoJ+KIkicsICItIiwgbmFtZTApCgoKbmFtZXMuYWxsID0gdW5pcXVlKGMocmVzLm5lc3Quc2ltJFYxW3Jlcy5uZXN0LnNpbSRWOCA9PSBuYW1lMF0sCiAgICAgICAgICAgICAgICAgICAgIHJlcy5uZXN0LnNpbSRWOFtyZXMubmVzdC5zaW0kVjEgPT0gbmFtZTBdKSkKIyBuYW1lcy5hbGwgPSB1bmlxdWUoYyhyZXMubmVzdCRWMVtyZXMubmVzdCRWOCA9PSBuYW1lMF0sIAojICAgICAgICAgICAgICAgICAgICAgIHJlcy5uZXN0JFY4W3Jlcy5uZXN0JFYxID09IG5hbWUwXSkpCgpwLmFsbCA9IGxpc3QoKQpmb3IobmFtZTIgaW4gbmFtZXMuYWxsKXsKICAjIG1lc3NhZ2UobmFtZTIpCiAgc2VxMSA9IHRlLmZhc3RhW1tuYW1lMF1dCiAgc2VxMiA9IHRlLmZhc3RhW1tuYW1lMl1dCiAgCiAgczEgPSBwYXN0ZTAoc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ3wnKQogIHMyID0gcGFzdGUwKHN0cnNwbGl0KG5hbWUyLCAnXFx8JylbWzFdXVs3OjldLCBjb2xsYXBzZSA9ICd8JykKICBwID0gZG90cGxvdChzZXExLCBzZXEyLCB3c2l6ZSwgbm1hdGNoKSArIHhsYWIoczEpICsgeWxhYihzMikKICBwLmFsbFtbbmFtZTJdXSA9IHAKfQoKCnMwID0gcGFzdGUwKHN0cnNwbGl0KG5hbWUwLCAnXFx8JylbWzFdXVs3OjldLCBjb2xsYXBzZSA9ICdfJykKczAgPSBnc3ViKCIvIiwgIi0iLCBzMCkKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3BhaXJ3aXNlX2Nvbm5lY3RfJyxzMCwnLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1MCwgaGVpZ2h0ID0gNTApCmdyaWQuYXJyYW5nZShncm9icyA9IHAuYWxsLCBuY29sID0gY2VpbGluZyhzcXJ0KGxlbmd0aChwLmFsbCkpKSkgIyBXcml0ZSB0aGUgZ3JpZC5hcnJhbmdlIGluIHRoZSBmaWxlCmRldi5vZmYoKSAjIENsb3NlIHRoZSBmaWxlCgpgYGAKCgoKYGBge3J9CgoKbmFtZTEgPSAndGV8MTQxODkyNTZ8MTQxOTAyNjZ8NXwxMDExfC18QVQ1VEU1MDcwMHxIRUxJVFJPTlkzfFJDL0hlbGl0cm9uJwpuYW1lMiA9ICd0ZXwyMTYyMjk1fDIxNjI5Mzd8Mnw2NDN8LXxBVDJURTA5OTUwfEhFTElUUk9OWTN8UkMvSGVsaXRyb24nCm5hbWUwID0gbmFtZTEKCm5hbWVzLmFsbCA9IHVuaXF1ZShjKHJlcy5uZXN0JFYxW3Jlcy5uZXN0JFY4ID09IG5hbWUwXSwgcmVzLm5lc3QkVjhbcmVzLm5lc3QkVjEgPT0gbmFtZTBdKSkKCgpuYW1lcyA9IGMobmFtZTEsIG5hbWUyKQpiLnRtcCA9IGJsLnJlc1soYmwucmVzJFYxICVpbiUgbmFtZXMpICYgKGJsLnJlcyRWOCAlaW4lIG5hbWVzKSxdCgpyZXMubmVzdFsocmVzLm5lc3QkVjEgJWluJSBuYW1lcykgJiAocmVzLm5lc3QkVjggJWluJSBuYW1lcyksIF0KCmBgYAoKCiMgU1ZzCiMjIFJlYWRpbmdzIHNlU1ZzCmBgYHtyfQoKc3Yuc2UgPSByZWFkUkRTKHBhc3RlKHBhdGguc3ZzLCAnc3Zfc2UucmRzJywgc2VwID0gJycpKQoKIyBSZW5hbWUgbGVuZ3RoIGdyb3VwcwpsZXYucmVwbGFjZSA9IGMoJ1sxLDEwXScsICcoMTAsMTVdJykKbGV2Lm5ldyA9ICdbMSwxNV0nCgpzLmxldmVscyA9IGFzLmNoYXJhY3RlcihsZXZlbHMoc3Yuc2UkbGVuLmdyKSkKcy5sZXZlbHMgPSBzLmxldmVsc1shKHMubGV2ZWxzICVpbiUgbGV2LnJlcGxhY2UpXQpzLmxldmVscyA9IGMobGV2Lm5ldywgcy5sZXZlbHMpCnMubGV2ZWxzID0gZ3N1YigiZVxcKzAzIiwgImsiLCBzLmxldmVscykKCnN2LnNlJGxlbi5nciA9IGFzLmNoYXJhY3Rlcihzdi5zZSRsZW4uZ3IpCnN2LnNlJGxlbi5ncltzdi5zZSRsZW4uZ3IgJWluJSBsZXYucmVwbGFjZV0gPSBsZXYubmV3CnN2LnNlJGxlbi5nciA9IGdzdWIoImVcXCswMyIsICJrIiwgc3Yuc2UkbGVuLmdyKQpzdi5zZSRsZW4uZ3IgPSBmYWN0b3Ioc3Yuc2UkbGVuLmdyLCBsZXZlbHMgPSBzLmxldmVscykKCgojIFJlcGxhY2UgZmFtaWxpZXMKc3Yuc2UkZmFtID0gYXMuY2hhcmFjdGVyKHN2LnNlJGZhbSkKc3Yuc2UkZmFtIDwtIGdzdWIoIkhlbGl0cm9uLy4qIiwgIk1peCB3aXRoIEhlbGl0cm9uIiwgc3Yuc2UkZmFtKQoKCnN2LnNlJHRlID0gZmFjdG9yKHN2LnNlJHRlLCBsZXZlbHMgPSBjKCdpc1RFJywgJ2lzVEVwYXJ0JywgJ2hhc1RFJywgJ2hhc1RFcGFydCcsICdub1RFJykpCgoKCmBgYAoKIyMgUmVhZGluZyBuZXN0ZWRuZXNzCmBgYHtyfQoKIyBMb2FkIHNpbWlsYXJpdHkgZnVuY3Rpb24KCmZpbGUubmVzdGVkbmVzcyA9IHBhc3RlKHBhdGgud29yaywgJ3N2X2JpZ19vbl9iaWdfbmVzdC5yZHMnLCBzZXAgPSAnJykKCgppZighZmlsZS5leGlzdHMoZmlsZS5uZXN0ZWRuZXNzKSl7CiAgYmwuZmlsZSA9IHBhc3RlKHBhdGgud29yaywgJ3N2X2JpZ19vbl9iaWcudHh0Jywgc2VwID0gJycpCiAgYmwucmVzID0gcmVhZC50YWJsZShibC5maWxlKQogIGJsLnJlcyA9IGJsLnJlc1tibC5yZXMkVjEgIT0gYmwucmVzJFY4LF0KCiAgcmVzLm5lc3QgPSBmaW5kTmVzdGVkbmVzcyhibC5yZXMsIHVzZS5zdHJhbmQgPSBGKQogICAgCiAgcmVzLm5lc3QkbGVuMSA9IHJlcy5uZXN0LmxlbltyZXMubmVzdCRWMV0KICByZXMubmVzdCRsZW44ID0gcmVzLm5lc3QubGVuW3Jlcy5uZXN0JFY4XQogIHJlcy5uZXN0JHAxID0gcmVzLm5lc3QkQzEgLyByZXMubmVzdCRsZW4xCiAgcmVzLm5lc3QkcDggPSByZXMubmVzdCRDOCAvIHJlcy5uZXN0JGxlbjggIAogIHNhdmVSRFMocmVzLm5lc3QsIGZpbGUubmVzdGVkbmVzcywgY29tcHJlc3MgPSBGKQp9IGVsc2UgewogIHJlcy5uZXN0ID0gcmVhZFJEUyhmaWxlLm5lc3RlZG5lc3MpCn0KCnJlcy5uZXN0LmxlbiA9IHNhcHBseSh1bmlxdWUoYyhyZXMubmVzdCRWMSwgcmVzLm5lc3QkVjgpKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihzKSBhcy5udW1lcmljKHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzJdKSkKcmVzLm5lc3QwID0gcmVzLm5lc3QKCgoKYGBgCgoKIyMgVEUgc3RhdAojIyMgSW4gZ3JhcGggLSBub3QgaW4gZ3JhcGgKYGBge3J9CnJlcy5uZXN0ID0gcmVzLm5lc3QwCgpzdi5zZS5sZW4gPSBzdi5zZVtzdi5zZSRsZW4gPj0gMTAwLF0Kc3Yuc2UubGVuJGluLmNvbm5lY3QgPSBzdi5zZS5sZW4kbmFtZSAlaW4lIG5hbWVzKHJlcy5uZXN0LmxlbikKCmNudC5zdi5zZSA9IHRhYmxlKHN2LnNlLmxlbiRpbi5jb25uZWN0ICwgc3Yuc2UubGVuJHRlKQpjbnQuc3Yuc2UKCmRmID0gcmVzaGFwZTI6Om1lbHQoY250LnN2LnNlKQoKdGUuY29udGVudC5uYW1lcyA9IGMoIm5vVEUiLCAiaXNURSIsICJoYXNURSIsICJoYXNURXBhcnQiLCAiaXNURXBhcnQiKQpjb2xzID0gYygnI0Q4RDlDRicsICcjRUI0NTVGJywgJyM3QjYwNzknLCAnIzNDOERBRCcsICcjNzlCNzczJykKbmFtZXMoY29scykgPSB0ZS5jb250ZW50Lm5hbWVzCgpkZiRWYXIyID0gZmFjdG9yKGRmJFZhcjIsIGxldmVscyA9IHJldihjKCdpc1RFJywgJ2lzVEVwYXJ0JywgJ2hhc1RFJywgJ2hhc1RFcGFydCcsICdub1RFJykpKQoKCiMgaW5zdGFsbC5wYWNrYWdlcygiZ2dwYXR0ZXJuIikKCgpwID0gZ2dwbG90KGRmLCBhZXMoeCA9IFZhcjIsIHkgPSB2YWx1ZSwgZmlsbCA9IFZhcjIsIGFscGhhID0gVmFyMSwgY29sb3IgPSBWYXIxKSkgKwogIGdlb21fY29sX3BhdHRlcm4oIGFlcyhwYXR0ZXJuID0gVmFyMSksCiAgICAjIHBhdHRlcm4gPSByZXAoYygnbm9uZScsICJzdHJpcGUiKSwgNSksCiAgICBwYXR0ZXJuX2RlbnNpdHkgPSAwLjEsCiAgICBwYXR0ZXJuX3NwYWNpbmcgPSAwLjAyNSwKICAgIHBhdHRlcm5fZmlsbCA9ICJncmV5NzAiLCAKICAgIHBvc2l0aW9uID0gImRvZGdlIiwgCiAgICB3aWR0aCA9IDAuOAogICkgKyAKICAjIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIiwgd2lkdGggPSAwLjgpICsKICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygwLjgsIDEpLCBsYWJlbHMgPSBjKCJObyIsICJZZXMiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdibGFjaycsICdibGFjaycpLCBsYWJlbHMgPSBjKCJub3QgaW4gZ3JhcGgiLCAiaW4gZ3JhcGgiKSkgKwogIHNjYWxlX3BhdHRlcm5fbWFudWFsKHZhbHVlcyA9IGMoInN0cmlwZSIsICdub25lJyksIGxhYmVscyA9IGMoImluIGdyYXBoIiwgIm5vdCBpbiBncmFwaCIpLAogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoVFJVRSwgRkFMU0UpKSArCiAgbGFicyhmaWxsID0gIiIsIHBhdHRlcm49J0Nvbm5lY3RlZCB0byBvdGhlcnMnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29scykgKwogIHhsYWIoTlVMTCkgKwogIHlsYWIoIk51bWJlciBvZiBTVnMiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgZ3VpZGVzKGFscGhhID0gIm5vbmUiLCBmaWxsID0gJ25vbmUnLCBjb2xvciA9ICdub25lJykgKwogIHRoZW1lX21pbmltYWwoKSArIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuNywgMC4zKSwgICAgICMgQWRqdXN0IHRoZXNlIGNvb3JkaW5hdGVzIGFzIG5lZWRlZAogICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0idHJhbnNwYXJlbnQiLCBjb2xvcj0nZ3JleTcwJykgIAogICkgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgZ3VpZGVzKHBhdHRlcm4gPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChmaWxsID0gYygid2hpdGUiKSwgY29sb3I9ICdibGFjaycpKSkgIApwCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2luX2dyYXBoLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSAzLCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKCgojIyMgVEUgZmFtaWxpZXMgaW4gU1YgdHlwZXMKYGBge3J9Cgpzdi5zZS5sZW4gPSBzdi5zZVtzdi5zZSRsZW4gPj0gMTAwLF0KY250LmZhbS5zdiA9IHRhYmxlKHN2LnNlLmxlbiRmYW1bc3Yuc2UubGVuJGZhbSE9JyddLCBzdi5zZS5sZW4kdGVbc3Yuc2UubGVuJGZhbSE9JyddKQpjbnQuZmFtLnN2ID0gdChhcHBseShjbnQuZmFtLnN2LCAxLCBmdW5jdGlvbih4KSB4L3N1bSh4KSkpCmNudC5mYW0uc3YgPSBjbnQuZmFtLnN2WywgY29sU3VtcyhjbnQuZmFtLnN2KSAhPSAwXQpjbnQuZmFtLnN2ID0gcmVzaGFwZTI6Om1lbHQoY250LmZhbS5zdikKCnAgPSBnZ3Bsb3QoY250LmZhbS5zdiwgYWVzKHggPSBWYXIyLCB5ID0gVmFyMSwgY29sb3IgPSBWYXIyKSkgKyAKICBnZW9tX3BvaW50KGFlcyhzaXplID0gdmFsdWUsIGFscGhhID0gdmFsdWUgKiAyKSkgKyB0aGVtZV9taW5pbWFsKCkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29scykgICsKICBnZW9tX3RleHQoZGF0YSA9IGNudC5mYW0uc3ZbY250LmZhbS5zdiR2YWx1ZSA+PSAwLjIsXSwgCiAgICAgICAgICAgICAgYWVzKHg9VmFyMiwgeT1WYXIxLCBsYWJlbCA9IHJvdW5kKHZhbHVlLCAyKSksIAogICAgICAgICAgICAgIHNpemUgPSAyLjUsIGNvbG9yID0gJ2JsYWNrJywgCiAgICAgICAgICAgIG51ZGdlX3ggPSAwLjMsCiAgICAgICAgICAgIG51ZGdlX3kgPSAwKSArCiAgZ3VpZGVzKHNpemUgPSAibm9uZSIsIGFscGhhID0gIm5vbmUiLCBjb2xvciA9ICdub25lJykgKwogIHhsYWIoJ1NWIHR5cGUnKSArIHlsYWIoJ1RFIGZhbWlseScpCnAKCgpjbnQuZmFtLnN2ID0gcm93U3Vtcyh0YWJsZShzdi5zZS5sZW4kZmFtW3N2LnNlLmxlbiRmYW0hPScnXSwgc3Yuc2UubGVuJHRlW3N2LnNlLmxlbiRmYW0hPScnXSkpCmNudC5mYW0uc3YgPSBkYXRhLmZyYW1lKHZhbHVlID0gY250LmZhbS5zdiwgbmFtZXMgPSBuYW1lcyhjbnQuZmFtLnN2KSkKcm93bmFtZXMoY250LmZhbS5zdikgPSBOVUxMCgpnID0gZ2dwbG90KGNudC5mYW0uc3YsIGFlcyh4ID0gbmFtZXMsIHkgPSB2YWx1ZSkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGw9ImdyZXk4MCIpKwogIGNvb3JkX2ZsaXAoKSArIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwYXN0ZSgiMWUiLHNlcSgwLDQsMSksIHNlcCA9ICcnKSwgYnJlYWtzPSBzZXEoMCw0LDEpKjEwMDApICsKICB5bGFiKCcjJykgKyBnZW9tX3RleHQoYWVzKGxhYmVsPXZhbHVlLCB5PTApLCBoanVzdD0wLCBzaXplID0gMi41KQpnIAoKCnBwID0gZ2dwdWJyOjpnZ2FycmFuZ2UocCArIHhsYWIoJ1RFIGNvbnRlbnQnKSArIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygnaXMgY29tcGwuJywgJ2lzIGZyYWdtLicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2NvbnQuIGNvbXBsLicsICdjb250LiBmcmFnbS4nKSkgLCBnLCBuY29sID0gMiwgd2lkdGhzID0gYygwLjc1LCAwLjI1KSkKcHAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2JfdGVfZmFtX3N2X3R5cGUucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCnByaW50KHBwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgojIEluc2VydGlvbiBhbmQgZGVsZXRpb24KaWR4ID0gKHN2LnNlLmxlbiRmYW0hPScnKSAmIChzdi5zZS5sZW4kZnJlcS5tYXggPD0gMykKdGFibGUoc3Yuc2UubGVuJGZhbVtpZHhdLCBzdi5zZS5sZW4kdGVbaWR4XSkKCmlkeCA9IChzdi5zZS5sZW4kZmFtIT0nJykgJiAoc3Yuc2UubGVuJGZyZXEubWF4ID49IDI1KSAmIChzdi5zZS5sZW4kbGVuID49IDEwMCkgCnRhYmxlKHN2LnNlLmxlbiRmYW1baWR4XSwgc3Yuc2UubGVuJHRlW2lkeF0pCmBgYAojIyMgVEUgZmFtOiBUQUlSMTAKYGBge3J9CmYudGUucmVmID0gcGFzdGUocGF0aC50YWlyLCAnbmV3X3RlLmZhc3RhJywgc2VwID0gJycpCmxpbmVzID0gcmVhZExpbmVzKGYudGUucmVmKQpsaW5lcyA9IGdyZXAoJ14+JywgbGluZXMsIHZhbHVlID0gVCkKCnJlZi5mYW0gPSBzYXBwbHkobGluZXMsIGZ1bmN0aW9uKHgpIHN0cnNwbGl0KHgsICdcXHwnKVtbMV1dWzldKQoKCmluZGljZXMgPC0gZ3JlcCgiXkROQSg/IS9IQVR8L011RFIpIiwgcmVmLmZhbSwgdmFsdWUgPSBGQUxTRSwgcGVybCA9IFRSVUUpCnJlZi5mYW1baW5kaWNlc10gPSAnRE5BKycKCmluZGljZXMgPC0gZ3JlcCgiXlJhdGhFIiwgcmVmLmZhbSwgdmFsdWUgPSBGQUxTRSwgcGVybCA9IFRSVUUpCnJlZi5mYW1baW5kaWNlc10gPSAnUmF0aEUxLzIvM19jb25zJwoKaW5kaWNlcyA8LSBncmVwKCJeTElORSIsIHJlZi5mYW0sIHZhbHVlID0gRkFMU0UsIHBlcmwgPSBUUlVFKQpyZWYuZmFtW2luZGljZXNdID0gJ0xJTkUnCnJlZi5mYW1bcmVmLmZhbSA9PSAnUkMvSGVsaXRyb24nXSA9ICdIZWxpdHJvbicKCnJlZi5mYW0uY250ID0gdGFibGUocmVmLmZhbSkKCgoKZGYgPSBjbnQuZmFtLnN2CmRmJHJlZiA9IGFzLm51bWVyaWMocmVmLmZhbS5jbnRbZGYkbmFtZXNdKQpkZiA9IGRmWyFpcy5uYShkZiRyZWYpLF0KCnBsb3QoZGYkdmFsdWUsIGRmJHJlZikKCgoKcCA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gcmVmLCB5ID0gdmFsdWUsIGNvbG9yID0gbmFtZXMpKSArCiAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gMSksIG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4geCwgc2UgPSBGQUxTRSwgY29sb3IgPSAnZ3JleTcwJykgKyAKICBnZW9tX3BvaW50KCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBuYW1lcyksIG1heC5vdmVybGFwcyA9IDIwKSArCiAgIyB4bGFiKCJsb2cgIyBpbiBUQUlSMTAgYW5ub3RhdGlvbiIpICsKICAjIHlsYWIoImxvZyAjIGluIFNWcyIpICsKICAjIHNjYWxlX3hfbG9nMTAoKSArCiAgIyBzY2FsZV95X2xvZzEwKCkgKwogIHhsYWIoIiMgaW4gVEFJUjEwIGFubm90YXRpb24iKSArCiAgeWxhYigiIyBpbiBTVnMiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICB0aGVtZV9taW5pbWFsKCkKcAoKCmxtX21vZGVsIDwtIGxtKHZhbHVlIH4gcmVmLCBkYXRhID0gZGYpCnNsb3BlIDwtIGNvZWYobG1fbW9kZWwpWzJdCgoKcCA9IHAgKyBhbm5vdGF0ZSgidGV4dCIsIHggPSBtaW4oZGYkcmVmKSwgeSA9IG1heChkZiR2YWx1ZSksIAogICAgICAgICAgIGxhYmVsID0gcGFzdGUoJ1Nsb3BlOicsIHJvdW5kKHNsb3BlLCAzKSksIGhqdXN0ID0gMCwgdmp1c3QgPSAxKQoKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX3RlX2ZhbV90YWlyMTAucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDQsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCmBgYAoKCgojIyBGaWx0cmF0aW9uCmBgYHtyfQoKcmVzLm5lc3QgPSByZXMubmVzdDAKCnN2Lm5hbWVzLm1peCA9IHN2LnNlJG5hbWVbZ3JlcCgiXk1peCIsIHN2LnNlJGZhbSldCnJlcy5uZXN0ID0gcmVzLm5lc3RbIShyZXMubmVzdCRWMSAlaW4lIHN2Lm5hbWVzLm1peCksXQpyZXMubmVzdCA9IHJlcy5uZXN0WyEocmVzLm5lc3QkVjggJWluJSBzdi5uYW1lcy5taXgpLF0KCgpzdi5uYW1lcy5taXggPSBzdi5zZSRuYW1lW3N2LnNlJHRlID09ICdub1RFJ10KcmVzLm5lc3QgPSByZXMubmVzdFshKHJlcy5uZXN0JFYxICVpbiUgc3YubmFtZXMubWl4KSxdCnJlcy5uZXN0ID0gcmVzLm5lc3RbIShyZXMubmVzdCRWOCAlaW4lIHN2Lm5hbWVzLm1peCksXQoKc2luZ2xldG9uLm1vZGUgPSBGCmlmKHNpbmdsZXRvbi5tb2RlKXsKICBzdi5uYW1lcy5mcmVxID0gc3Yuc2UkbmFtZVtzdi5zZSRmcmVxLm1heCA8PSAzXQogICMgc3YubmFtZXMuZnJlcSA9IHN2LnNlJG5hbWVbc3Yuc2UkZnJlcS5tYXggPj0gMjVdCiAgcmVzLm5lc3QgPSByZXMubmVzdFtyZXMubmVzdCRWMSAlaW4lIHN2Lm5hbWVzLmZyZXEsXQogIHJlcy5uZXN0ID0gcmVzLm5lc3RbcmVzLm5lc3QkVjggJWluJSBzdi5uYW1lcy5mcmVxLF0KfQoKcHJlZml4Lm1vZGUgPSBjKCcnLCAnX3NpbmdsZScpCgoKYGBgCgoKIyMgR3JhcGgKYGBge3J9CiMgYWxsIGVkZ2VzCmlkeCA9IHJlcy5uZXN0JHAxID49IHNpbS5jdXRvZmYKZWRnZXMgPSBjYmluZChyZXMubmVzdCRWMVtpZHhdLCByZXMubmVzdCRWOFtpZHhdKQppZHggPSByZXMubmVzdCRwOCA+PSBzaW0uY3V0b2ZmCmVkZ2VzID0gcmJpbmQoZWRnZXMsIGNiaW5kKHJlcy5uZXN0JFY4W2lkeF0sIHJlcy5uZXN0JFYxW2lkeF0pKQp0ZS5lbmdlcy5uYW1lcyA9IHVuaXF1ZShjKGVkZ2VzWywxXSwgZWRnZXNbLDJdKSkKCnRtcCA9IHN2LnNlJHRlCm5hbWVzKHRtcCkgPSBzdi5zZSRuYW1lCnRlLmVuZ2VzLnR5cGUgPSBhcy5jaGFyYWN0ZXIodG1wW3RlLmVuZ2VzLm5hbWVzXSkKbmFtZXModGUuZW5nZXMudHlwZSkgPC0gbmFtZXModG1wW3RlLmVuZ2VzLm5hbWVzXSkKCgp0bXAgPSBzdi5zZSRmYW0KbmFtZXModG1wKSA9IHN2LnNlJG5hbWUKdGUuZW5nZXMuZmFtID0gdG1wW3RlLmVuZ2VzLm5hbWVzXQoKIyBub2RlcwppZHggPSAocmVzLm5lc3QkcDEgPj0gc2ltLmN1dG9mZikgJiAocmVzLm5lc3QkcDggPj0gc2ltLmN1dG9mZikKdGUubm9kZXMgPSBjYmluZChyZXMubmVzdCRWMVtpZHhdLCByZXMubmVzdCRWOFtpZHhdKQp0ZS5yZXN0ID0gc2V0ZGlmZih0ZS5lbmdlcy5uYW1lcywgYyh0ZS5ub2Rlc1ssMV0sIHRlLm5vZGVzWywyXSkpCgoKdGUubm9kZXMuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQodGUubm9kZXMpLCBkaXJlY3RlZCA9IFQpCnRlLm5vZGVzLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodGUubm9kZXMuZ3JhcGgpCnRlLm5vZGVzLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLm5vZGVzLmdyYXBoKQoKbm9kZXMgPSBkYXRhLmZyYW1lKG5vZGUgPSBwYXN0ZSgnTicsIHRlLm5vZGVzLmNvbXAkbWVtYmVyc2hpcCwgc2VwID0gJycpLCB0ZSA9IG5hbWVzKHRlLm5vZGVzLmNvbXAkbWVtYmVyc2hpcCkpCgpub2Rlcy5yZXN0ID0gZGF0YS5mcmFtZShub2RlID0gcGFzdGUoJ1InLCAoMTpsZW5ndGgodGUucmVzdCkpLCBzZXAgPSAnJyksIHRlID0gdGUucmVzdCkKbm9kZXMgPSByYmluZChub2Rlcywgbm9kZXMucmVzdCkKCnJvd25hbWVzKG5vZGVzKSA9IG5vZGVzJHRlCgojIERlZmluZSBURSB0eXBlCm5vZGVzLmNudCA9IGRhdGEuZnJhbWUoY250ID0gYyh0YWJsZShub2RlcyRub2RlKSkpCm5vZGVzLmNudCRub2RlID0gcm93bmFtZXMobm9kZXMuY250KQpub2Rlcy5jbnQkdHlwZSA9IHNhcHBseShub2Rlcy5jbnQkbm9kZSwgZnVuY3Rpb24ocyl7CiAgcy50ZSA9IG5vZGVzJHRlW25vZGVzJG5vZGUgPT0gc10KICB0eXBlLnRlID0gdW5pcXVlKHRlLmVuZ2VzLnR5cGVbcy50ZV0pCiAgaWYobGVuZ3RoKHR5cGUudGUpID09IDEpewogICAgcmV0dXJuKHR5cGUudGUpCiAgfSBlbHNlIHsKICAgIHR5cGUudGUgPSB0YWJsZSh0eXBlLnRlKQogICAgdHlwZS50ZSA9IG5hbWVzKHR5cGUudGUpW3R5cGUudGUgPT0gbWF4KHR5cGUudGUpXQogICAgcmV0dXJuKHR5cGUudGVbMV0pCiAgfQp9KQp0YWJsZShub2Rlcy5jbnQkdHlwZSkKCiMgRGVmaW5lIFRFIGZhbWlseQpub2Rlcy5jbnQkZmFtID0gc2FwcGx5KG5vZGVzLmNudCRub2RlLCBmdW5jdGlvbihzKXsKICBzLnRlID0gbm9kZXMkdGVbbm9kZXMkbm9kZSA9PSBzXQogIHR5cGUudGUgPSB1bmlxdWUodGUuZW5nZXMuZmFtW3MudGVdKQogIGlmKGxlbmd0aCh0eXBlLnRlKSA9PSAxKXsKICAgIHJldHVybih0eXBlLnRlKQogIH0gZWxzZSB7CiAgICB0eXBlLnRlID0gdGFibGUodHlwZS50ZSkKICAgIHR5cGUudGUgPSBuYW1lcyh0eXBlLnRlKVt0eXBlLnRlID09IG1heCh0eXBlLnRlKV0KICAgIHJldHVybih0eXBlLnRlWzFdKQogIH0KfSkKdGFibGUobm9kZXMuY250JGZhbSkKCgojIFJlZGVmaW5lIGVkZ2VzIGJ1dCB3aXRoIG5vZGUgbmFtZXMKaWR4LmVuZGVzID0gKGVkZ2VzWywxXSAlaW4lIG5vZGVzJHRlKSAmIChlZGdlc1ssMl0gJWluJSBub2RlcyR0ZSkKYi5ncmFwaCA9IGNiaW5kKG5vZGVzW2VkZ2VzW2lkeC5lbmRlcywxXSwgJ25vZGUnXSxub2Rlc1tlZGdlc1tpZHguZW5kZXMsMl0sICdub2RlJ10pCmIuZ3JhcGggPSB1bmlxdWUoYi5ncmFwaCkKIyBiLmdyYXBoID0gYi5ncmFwaFtiLmdyYXBoWywxXSAhPSBiLmdyYXBoWywyXSxdCmIuZ3JhcGgudW5pID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoWywyXSxdCmIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KCmxlbmd0aCh1bmlxdWUoYyhiLmdyYXBoWywxXSwgYi5ncmFwaFssMl0pKSkKCiMgcmVkdWNlIGluZGlyZWN0IGFycm93cwppZHgucmVtb3ZlID0gYygpCmZvcihpLmVkZ2UgaW4gMTpucm93KGIuZ3JhcGgpKXsKICBpZihpLmVkZ2UgJSUgMTAwMCA9PSAwKSBwcmludChpLmVkZ2UpCiAgdG1wLnRvID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoW2kuZWRnZSwxXSwyXQogIHRtcC5mcm9tID0gYi5ncmFwaFtiLmdyYXBoWywyXSA9PSBiLmdyYXBoW2kuZWRnZSwyXSwxXQogIGlmKGxlbmd0aChpbnRlcnNlY3QodG1wLnRvLCB0bXAuZnJvbSkpID4gMCkgaWR4LnJlbW92ZSA9IGMoaWR4LnJlbW92ZSwgaS5lZGdlKQp9CmlkeC5yZW1vdmUgPSB1bmlxdWUoaWR4LnJlbW92ZSkKYi5ncmFwaCA9IGIuZ3JhcGhbLWlkeC5yZW1vdmUsXQojIGIuZ3JhcGggPSByYmluZChiLmdyYXBoLCBiLmdyYXBoLnVuaSkKCiMgUHJpbnQgZ3JhcGgKCmcubm9kZXMudHlwZSA9IG5vZGVzLmNudCR0eXBlCm5hbWVzKGcubm9kZXMudHlwZSkgPSBub2Rlcy5jbnQkbm9kZQpnLm5vZGVzLmNudCA9IG5vZGVzLmNudCRjbnQKbmFtZXMoZy5ub2Rlcy5jbnQpID0gbm9kZXMuY250JG5vZGUKZy5ub2Rlcy5mYW0gPSBub2Rlcy5jbnQkZmFtCm5hbWVzKGcubm9kZXMuZmFtKSA9IG5vZGVzLmNudCRub2RlCgoKZy5jb2xzLm5hbWVzID0gYygibm9URSIsICJpc1RFIiwgImhhc1RFIiwgImhhc1RFcGFydCIsICJpc1RFcGFydCIpCmcuY29scyA9IGMoJyNGRkQ5NjYnLCAnI0VCNDU1RicsICcjN0I2MDc5JywgJyMzQzhEQUQnLCAnIzc5Qjc3MycpCm5hbWVzKGcuY29scykgPSBnLmNvbHMubmFtZXMKCgpnLnBhcnQgPC0gbmV0d29yayhiLmdyYXBoLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKYi5ncmFwaC5uYW1lcyA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydCkKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy50eXBlW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICAjIGFycm93LmdhcCA9IDAsIAogICAgICAgICAgICAjIGFycm93LnNpemUgPSAzLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzKSArIGd1aWRlcyhzaXplID0gRikKcAoKIyBwYXRoLmZpZ3VyZXMgID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya190ZS8nCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2JfYWxsX2NsdXN0ZXInLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ190eXBlLnBkZicsIHNlcCA9ICcnKSwgCiAgICB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCiMgc2V0LnNlZWQoMjApCiMgcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiZ3JleTMwIiwgCiMgICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAojICAgICAgICAgICAgIGNvbG9yID0gYygnVEUnLCAnbm9URScpWyhnLm5vZGVzLnR5cGVbYi5ncmFwaC5uYW1lc10gPT0gJ25vVEUnKSoxKzFdLAojICAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiMgICAgICAgICAgICAgIyBhcnJvdy5nYXAgPSAwLCAKIyAgICAgICAgICAgICAjIGFycm93LnNpemUgPSAzLAojICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCdub1RFJyA9ICdibGFjaycsICdURScgPSAnI0FFQzNBRScpKSArIGd1aWRlcyhzaXplID0gRikKIyBwCiMgCiMgIyBwYXRoLmZpZ3VyZXMgID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya190ZS8nCiMgcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9hbGxfY2x1c3RlcicsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX3R5cGUucGRmJywgc2VwID0gJycpLCAKIyAgICAgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQojIHByaW50KHArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCiMgZGV2Lm9mZigpCgpgYGAKCgojIyMgQ29sb3JlZCBieSBURSBmYW1pbHkKYGBge3J9CgppZihsZW5ndGgoc2V0ZGlmZihnLm5vZGVzLmZhbSwgbmFtZXMoZmFtLnBhbGV0dGUpKSkhPTApIHN0b3AoJ25vdCBhbGwgZmFtaWxpZXMgYXJlIGRlZmluZWQnKQoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImdyZXkyMCIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgICMgYXJyb3cuZ2FwID0gMCwgCiAgICAgICAgICAgICMgYXJyb3cuc2l6ZSA9IDMsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgPSBwICsgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLCAKICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41LCAiY20iKSkgKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5LnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2NsdXN0ZXInLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19mYW1pbHlfbGVnZW5kLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA3LCBoZWlnaHQgPSA1KQpwcmludChwKyBjb29yZF9maXhlZChyYXRpbyA9IDEpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgpgYGAKCgojIyMgTm9kZSBzaXplIGRpc3RyaWJ1dGlvbgpgYGB7cn0KZGYgPSBkYXRhLmZyYW1lKG5vZGUgPSB1bmlxdWUobm9kZXMkbm9kZSkpCmRmJHNpemUgPSBnLm5vZGVzLmNudFtkZiRub2RlXQpkZiRmYW0gPSBnLm5vZGVzLmZhbVtkZiRub2RlXQpkZiR0eXBlID0gZy5ub2Rlcy50eXBlW2RmJG5vZGVdCgpmYW0ucGFsZXR0ZQoKcCA9IGdncGxvdChkZiwgYWVzKHggPSB0eXBlLCB5ID0gc2l6ZSwgY29sb3I9ZmFtKSkgKwogIGdlb21faml0dGVyKHdpZHRoID0gMC4yKSArCiAgbGFicyh4ID0gIlR5cGUiLCB5ID0gIlNpemUiKSArIAogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBmYW0ucGFsZXR0ZSkrCiAgdGhlbWVfbWluaW1hbCgpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKSArCiAgbGFicyhjb2xvciA9ICJURSBmYW1pbHkiKSArIHhsYWIoJycpICsgeWxhYignTm9kZSBzaXplIChOdW1iZXIgb2Ygc2ltaWxhciBTVnMpJykKcAoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2Jfc2l6ZV9kaXN0cmlidXRpb24ucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDYuNSwgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpgYGAKCgoKIyMjIFNlcGFyYXRlbHkgdmlzdWFsaXNlIGNvbm5lY3RlZCBjb21wb25lbnRzCmBgYHtyfQp0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKdG1wLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodG1wLmdyYXBoKQp0bXAuY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHModG1wLmdyYXBoKQoKdG1wLmNudCA9IHRhYmxlKHRtcC5jb21wJG1lbWJlcnNoaXApCnRtcC5jbnQgPSAtc29ydCgtdG1wLmNudCkKaGVhZCh0bXAuY250KQoKayA9IDEKdG1wLmsgPSBhcy5udW1lcmljKG5hbWVzKHRtcC5jbnQpW2tdKQp0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwID09IHRtcC5rXQpiLmdyYXBoLnN1YiA9IGIuZ3JhcGhbKGIuZ3JhcGhbLDFdICVpbiUgdG1wLm5hbWVzKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAoYi5ncmFwaFssMl0gJWluJSB0bXAubmFtZXMpLF0KCmcucGFydC5zdWIuYmlnIDwtIG5ldHdvcmsoYi5ncmFwaC5zdWIsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzLnN1Yi5iaWcgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQuc3ViLmJpZykKCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5iaWcsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLnR5cGVbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwKICAgICAgICAgICAgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgIHBhbGV0dGUgPSBnLmNvbHMpICsgZ3VpZGVzKHNpemUgPSBGKQpwLmJpZy50eXBlID0gcCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLmJpZywgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiAgICAgICAgICAgIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKQpwLmJpZy5jb2xvciA9IHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKdG1wLmsgPSBhcy5udW1lcmljKG5hbWVzKHRtcC5jbnQpW2tdKQp0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwICE9IHRtcC5rXQpiLmdyYXBoLnN1YiA9IGIuZ3JhcGhbKGIuZ3JhcGhbLDFdICVpbiUgdG1wLm5hbWVzKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAoYi5ncmFwaFssMl0gJWluJSB0bXAubmFtZXMpLF0KCmcucGFydC5zdWIuc21hbGwgPC0gbmV0d29yayhiLmdyYXBoLnN1YiwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMuc3ViLnNtYWxsID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0LnN1Yi5zbWFsbCkKCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5zbWFsbCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy50eXBlW2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwKICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scykgKyBndWlkZXMoc2l6ZSA9IEYpCnAuc21hbGwudHlwZSA9cCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLnNtYWxsLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sCiAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCnAuc21hbGwuY29sb3IgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKCgpgYGAKCiMjIyMgU2F2ZQpgYGB7cn0KIyBwLmJpZy50eXBlCiMgcC5iaWcuY29sb3IKIyBwLnNtYWxsLnR5cGUKIyBwLnNtYWxsLmNvbG9yCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9iaWdfY2x1c3RlcicsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX3R5cGUucGRmJywgc2VwID0gJycpLCAKICAgIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocC5iaWcudHlwZSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2JpZ19jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5LnBkZicsIHNlcCA9ICcnKSwgCiAgICB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHAuYmlnLmNvbG9yKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2Jfc21hbGxfY2x1c3RlcicsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX3R5cGUucGRmJywgc2VwID0gJycpLCAKICAgIHdpZHRoID0gNiwgaGVpZ2h0ID0gNikKcHJpbnQocC5zbWFsbC50eXBlKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2Jfc21hbGxfY2x1c3RlcicsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX2ZhbWlseS5wZGYnLCBzZXAgPSAnJyksIAogICAgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQpwcmludChwLnNtYWxsLmNvbG9yKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgoKYGBgCgoKCgojIyMgUnVuIGJ5IGFjY2Vzc2lvbnMKYGBge3J9CnBhdGguZmlndXJlcy5hY2MgPSAnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3RlL2ZpZ3VyZXNfdGVncmFwaF9hY2Nlc3Npb25zLycKc3YuYmluID0gcmVhZC50YWJsZSgnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3N2L3N2c19zZV9iaW5fdjAzLnR4dCcsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGLCBjaGVjay5uYW1lcyA9IEZBTFNFKQpgYGAKCgpgYGB7cn0KIyBhY2MgPSAnMTAwMDInCgpmb3IoYWNjIGluIGNvbG5hbWVzKHN2LmJpbikpewogIHN2LmFjYyA9IHJvd25hbWVzKHN2LmJpbilbc3YuYmluWyxhY2NdID09IDFdCiAgcm93bmFtZXMoc3Yuc2UpID0gc3Yuc2UkZ3IKICBzdi5hY2MgPSBzdi5zZVtzdi5hY2MsICduYW1lJ10KICAKICBzdi5hY2MgPSBpbnRlcnNlY3Qoc3YuYWNjLCByb3duYW1lcyhub2RlcykpCiAgbm9kZXMuY250LmFjYyA9IHRhYmxlKG5vZGVzW3N2LmFjYywnbm9kZSddKQogIAogIAogIHN2LmFscGhhID0gcmVwKDAsIGxlbmd0aChiLmdyYXBoLm5hbWVzKSkKICBuYW1lcyhzdi5hbHBoYSkgPSBiLmdyYXBoLm5hbWVzCiAgc3YuYWxwaGFbbmFtZXMoc3YuYWxwaGEpICVpbiUgbmFtZXMobm9kZXMuY250LmFjYyldID0gMQogIAogICMgc2V0LnNlZWQoMjM5KQogICMgcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAjICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAjICAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgIyAgICAgICAgICAgICBhbHBoYSA9IHN2LmFscGhhLAogICMgICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAjICAgICAgICAgICAgICMgYXJyb3cuZ2FwID0gMCwgCiAgIyAgICAgICAgICAgICAjIGFycm93LnNpemUgPSAzLAogICMgICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgCiAgc2V0LnNlZWQoMjApCiAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5zbWFsbCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLAogICAgICAgICAgICBhbHBoYSA9IHN2LmFscGhhW2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwKICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgogIHBkZihwYXN0ZShwYXRoLmZpZ3VyZXMuYWNjLCAnZ3JhcGhfdGUnLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19zbWFsbF9hY2NfJyxhY2MsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKICBwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgogIGRldi5vZmYoKQogIAogIAogIHNldC5zZWVkKDIwKQogIHAgPC0gZ2duZXQyKGcucGFydC5zdWIuYmlnLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwKICAgICAgICAgICAgYWxwaGEgPSBzdi5hbHBoYVtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLAogICAgICAgICAgICBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgogIHBkZihwYXN0ZShwYXRoLmZpZ3VyZXMuYWNjLCAnZ3JhcGhfdGUnLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19iaWdfYWNjXycsYWNjLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCiAgcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKICBkZXYub2ZmKCkKCn0KCnAgCgoKCmBgYAoKCmBgYHtyfQpzdi5hbm5vdCA9IHJlYWQudGFibGUoJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya19zdi9zdnNfYW5ub3RhdGlvbl92MDMudHh0Jywgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnJvd25hbWVzKHN2LmFubm90KSA9IHN2LmFubm90JGdyCmhlYWQoc3YuYW5ub3QpCgpzdi5hbm5vdFtleHRyYWN0ZWRfdmFsdWVzLF0KCmBgYAoKCgoKIyBTdG9wCmBgYHtyfQpzdG9wKCkKYGBgCgoKIyBCaWcgVEUtbm9kZXMKYGBge3J9Cm4uYW1vdW50ID0gMjAKCmcucGFydCA8LSBuZXR3b3JrKGIuZ3JhcGgsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0KQoKc2l6ZS5iaWcgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXQphbHBoYS5iaWcgPSByZXAoMSwgbGVuZ3RoKGIuZ3JhcGgubmFtZXMpKQpuYW1lcyhhbHBoYS5iaWcpID0gYi5ncmFwaC5uYW1lcwphbHBoYS5iaWdbc2l6ZS5iaWcgPCBuLmFtb3VudF0gPSAwCgpzdW0oc2l6ZS5iaWcgPj0gbi5hbW91bnQpCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gc2l6ZS5iaWcsIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBhbHBoYT0gYWxwaGEuYmlnLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICAjIGFycm93LmdhcCA9IDAsIAogICAgICAgICAgICAjIGFycm93LnNpemUgPSAzLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpCnAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9zbWFsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5X2Ftb3VudC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocCsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKCgoKYGBgCgojIyBXaGljaCBmYW1pbGllcyBzcGVjaWZpY2FsbHksIGFuZCBpcyB0aGUgcmF0ZSBvZiBpbnNlcnRpb24gaXMgZGlmZmVyZW50Pwpjb21wYXJlIG51bWJlciBvZiBpbnNlcnRpb25zIHdpdGggdGhlIHRvdGFsIG51bWJlciBvZiBURSBsb2FkCmBgYHtyfQoKYmlnLmZhbWlsaWVzID0gZGF0YS5mcmFtZShub2RlID0gIG5hbWVzKHNpemUuYmlnKVtzaXplLmJpZyA+PSBuLmFtb3VudF0pCmJpZy5mYW1pbGllcyRzaXplID0gc2l6ZS5iaWdbYmlnLmZhbWlsaWVzJG5vZGVdCmJpZy5mYW1pbGllcyRmYW0gPSBnLm5vZGVzLmZhbVtiaWcuZmFtaWxpZXMkbm9kZV0KYmlnLmZhbWlsaWVzID0gYmlnLmZhbWlsaWVzW29yZGVyKC1iaWcuZmFtaWxpZXMkc2l6ZSksXQpyb3duYW1lcyhiaWcuZmFtaWxpZXMpID0gTlVMTAoKbm9kZS5iaWcgPSBub2Rlc1tub2RlcyRub2RlICVpbiUgYmlnLmZhbWlsaWVzJG5vZGUsXQoKdiA9IHJlYWQudGFibGUocGFzdGUocGF0aC53b3JrLCAnYmxhc3Rfc3Zfb25fdGVzLnR4dCcsIHNlcCA9ICcnKSkKdiA9IHZbdiRWMSAlaW4lIG5vZGUuYmlnJHRlLF0KCgpwb3MubGVuMSA9IDIKcG9zLmxlbjIgPSA1CnYxLmxlbiA9IHNhcHBseSh1bmlxdWUodiRWMSksIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywnXFx8JylbWzFdXVtwb3MubGVuMV0pKQp2OC5sZW4gPSBzYXBwbHkodW5pcXVlKHYkVjgpLCBmdW5jdGlvbihzKSBhcy5udW1lcmljKHN0cnNwbGl0KHMsJ1xcfCcpW1sxXV1bcG9zLmxlbjJdKSkKdi5sZW4gPSBjKHYxLmxlbiwgdjgubGVuKQoKdi5zaW0gPSBmaW5kTmVzdGVkbmVzcyh2LCB1c2Uuc3RyYW5kID0gRikKCnYuc2ltID0gZmluZE5lc3RlZG5lc3ModiwgdXNlLnN0cmFuZCA9IEYpCnYuc2ltJHAxID0gdi5zaW0kQzEgLyB2Lmxlblt2LnNpbSRWMV0Kdi5zaW0kcDggPSB2LnNpbSRDOCAvIHYubGVuW3Yuc2ltJFY4XQp2LnNpbSRwMS5pbjggPSB2LnNpbSRDMSAvIHYubGVuW3Yuc2ltJFY4XQp2LnNpbSRwOC5pbjEgPSB2LnNpbSRDOCAvIHYubGVuW3Yuc2ltJFYxXQoKbm9kZS5iaWckc3ViZmFtID0gJycKZm9yKHN2Lm5hbWUgaW4gdW5pcXVlKHYuc2ltJFYxKSl7CiAgdi50bXAgPSB2LnNpbVt2LnNpbSRWMSA9PSBzdi5uYW1lLF0KICBzID0gdi50bXAkVjhbd2hpY2gubWF4KHYudG1wJHAxKV0KICBzID0gc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bOF0KICBub2RlLmJpZ1tzdi5uYW1lLCAnc3ViZmFtJ10gPSBzCn0KCgp4ID0gdGFwcGx5KG5vZGUuYmlnJHN1YmZhbSwgbm9kZS5iaWckbm9kZSwgZnVuY3Rpb24oeCl7CiAgY250ID0gdGFibGUoeCkKICB4ID0gbmFtZXMoY250KVtjbnQgPT0gbWF4KGNudCldCiAgcmV0dXJuKHBhc3RlMCh4LCBjb2xsYXBzZSA9ICAnLCcpKQp9KQoKYmlnLmZhbWlsaWVzJHN1YmZhbSA9IHhbYmlnLmZhbWlsaWVzJG5vZGVdCgoKYGBgCgoKCiMgbm8tVEUgU1YKIyMgQ29uc3RydWN0CmBgYHtyfQpzdi5zZSA9IHJlYWRSRFMocGFzdGUocGF0aC5zdnMsICdzdl9zZS5yZHMnLCBzZXAgPSAnJykpCnNpbS5jdXRvZmYgPSAwLjg1CgoKc3Yuc2Uubm8udGUgPSBzdi5zZSRuYW1lWyhzdi5zZSR0ZSA9PSAnbm9URScpICYgKHN2LnNlJGxlbiA+IDUwKV0KCmJsLmZpbGUgPSBwYXN0ZShwYXRoLndvcmssJ3N2X2JpZ19vbl9iaWcudHh0Jywgc2VwID0gJycpCmJsLnN2ID0gcmVhZC50YWJsZShibC5maWxlLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKYmwuc3YgPSBibC5zdltibC5zdiRWMSAhPSBibC5zdiRWOCxdCgojIHJlbW92ZSBoYXZpbmcgVEVzCmJsLnN2ID0gYmwuc3ZbYmwuc3YkVjEgJWluJSBzdi5zZS5uby50ZSwgXQpibC5zdiA9IGJsLnN2W2JsLnN2JFY4ICVpbiUgc3Yuc2Uubm8udGUsIF0KCnBvcy5sZW4xID0gMgpzdi5sZW4gPSBzYXBwbHkodW5pcXVlKGMoYmwuc3YkVjEsIGJsLnN2JFY4KSksIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywnXFx8JylbWzFdXVtwb3MubGVuMV0pKQpibC5zdiRsZW4xID0gc3YubGVuW2JsLnN2JFYxXQpibC5zdiRsZW44ID0gc3YubGVuW2JsLnN2JFY4XQptYXgubGVuID0gMjAwMDAKYmwuc3YgPSBibC5zdlsoYmwuc3YkbGVuMSA8PSBtYXgubGVuKSAmIChibC5zdiRsZW44IDw9IG1heC5sZW4pLF0KYmwuc3YkcDEgPSAoYmwuc3YkVjMgLSBibC5zdiRWMiArIDEpIC8gYmwuc3YkbGVuMQpibC5zdiRwOCA9IChhYnMoYmwuc3YkVjUgLSBibC5zdiRWNCkgKyAxKSAvIGJsLnN2JGxlbjgKYmwuc3YkY29tYiA9IGFzLmZhY3RvcihwYXN0ZShibC5zdiRWMSwgYmwuc3YkVjgsIHNlcCA9ICd8fCcpKQoKaWR4Lm11dHVhbCA9IChibC5zdiRwMSA+PSBzaW0uY3V0b2ZmKSAmIChibC5zdiRwOCA+PSBzaW0uY3V0b2ZmKQojIFRoZXJlIGlzIGEgYmlnIGRpc2N1c3Npb24gaW4gbXkgaGVhZCwgd2hldGhlciBpdCBzaG91bGQgYmUgJyYnIG9yICd8JwojIElmIGl0J3Mgbm90ICx1dHVhbCwgdGhlbiBtYXliZSB3aXRoIHNvbWV0aGluZyBlbHNlIGl0IHdpbGwgY29uc3RydWN0IGEgbXV0dWFsIHJlbGF0aW9uLCAKIyBzbyB3ZSBzaG91bGQgcmVtYWluIGZvciB0aGUgYW5hbHlzaXMgb2YgbmVzdGVkbmVzcyBhbGwgcGFydGlhbCBpbmNsdXNpb25zCnN2Lm11dHVhbCA9IGJsLnN2W2lkeC5tdXR1YWwsIF0KdiA9IGJsLnN2WyFpZHgubXV0dWFsLCBdCnYgPSB2WyEodiRjb21iICVpbiUgc3YubXV0dWFsJGNvbWIpLF0KCiMgQXQgc29tZSBwb2ludCBpdCB3YXMgYSBzdGVwIHRvIHJlbWFpbiBvbmx5IHRob3NlIGluc3RhbmNlcyB3aGljaCBhcmUgbm90ICJ1bmlxdWUiIGluIGNvbWJpbmF0aW9ucwojIGJ1dCBJIHRoaW5rIGl0J3Mgbm90IGNvcnJlY3QgaGVyZQoKc3Yuc2ltID0gZmluZE5lc3RlZG5lc3ModiwgdXNlLnN0cmFuZCA9IFQpCnN2LnNpbSRwMSA9IHN2LnNpbSRDMSAvIHN2Lmxlbltzdi5zaW0kVjFdCnN2LnNpbSRwOCA9IHN2LnNpbSRDOCAvIHN2Lmxlbltzdi5zaW0kVjhdCgojIGhlcmUgIHdlIHNob3VsZCBmaW5hbGx5IHVzZSAnfCcsIG5vdCAnJicKc3YubmVzdGVkID0gc3Yuc2ltWyhzdi5zaW0kcDEgPj0gc2ltLmN1dG9mZikgfCAoc3Yuc2ltJHA4ID49IHNpbS5jdXRvZmYpICxdCgojIENyZWF0ZSBwcmUtZGF0YSBmb3IgZGVmaW5pbmcgZWRnZXMKY29tbW9uLm5hbWVzID0gaW50ZXJzZWN0KGNvbG5hbWVzKHN2Lm11dHVhbCksIGNvbG5hbWVzKHN2Lm5lc3RlZCkpCnN2Lm92ZXJhbGwgPSByYmluZChzdi5tdXR1YWxbLGNvbW1vbi5uYW1lc10sIHN2Lm5lc3RlZFssY29tbW9uLm5hbWVzXSkKc3Yub3ZlcmFsbCRncm91cCA9IChzdi5vdmVyYWxsJHAxID49IHNpbS5jdXRvZmYpICogMSArIChzdi5vdmVyYWxsJHA4ID49IHNpbS5jdXRvZmYpICogMgppZHgxID0gc3Yub3ZlcmFsbCRncm91cCAhPSAyICAjIFYxIGluIFY4CmlkeDIgPSBzdi5vdmVyYWxsJGdyb3VwICE9IDEgICMgVjggaW4gVjEKCgojIEVkZ2VzIApzdi5lZGdlcyA9IHJiaW5kKGNiaW5kKHN2Lm92ZXJhbGwkVjFbaWR4MV0sIHN2Lm92ZXJhbGwkVjhbaWR4MV0pLAogICAgICAgICAgICAgICAgIGNiaW5kKHN2Lm92ZXJhbGwkVjhbaWR4Ml0sIHN2Lm92ZXJhbGwkVjFbaWR4Ml0pKQoKCnN2LmdyYXBoIDwtIGlncmFwaDo6bWFrZV9ncmFwaCh0KHN2LmVkZ2VzKSwgZGlyZWN0ZWQgPSBUKQpzdi5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHN2LmdyYXBoKQpzdi5ncmFwaGNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHN2LmdyYXBoKQoKc3YubWVtYiA9IGRhdGEuZnJhbWUobWVtYiA9IHN2LmdyYXBoY29tcCRtZW1iZXJzaGlwKQpzdi5tZW1iJG5hbWUgPSByb3duYW1lcyhzdi5tZW1iKQpyb3duYW1lcyhzdi5tZW1iKSA9IE5VTEwKcm93bmFtZXMoc3Yuc2UpID0gc3Yuc2UkbmFtZQpzdi5tZW1iJHRlID0gc3Yuc2Vbc3YubWVtYiRuYW1lLCAndGUnXQpzdi5tZW1iJGNvdmVyID0gc3Yuc2Vbc3YubWVtYiRuYW1lLCAnY292ZXInXSAvIHN2LnNlW3N2Lm1lbWIkbmFtZSwgJ2xlbiddCnN2Lm1lbWIkbGVuID0gc3YubGVuW3N2Lm1lbWIkbmFtZV0KYGBgCgojIyBQbG90IGFsbApgYGB7cn0KZy5wYXJ0IDwtIG5ldHdvcmsoc3YuZWRnZXMsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0KQoKc2V0LnNlZWQoMjM5KQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSAxLAogICAgICAgICAgICAjIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAgICAgICAgICAgIyBjb2xvciA9IGcubm9kZXMudHlwZVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgIyBwYWxldHRlID0gZy5jb2xzCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgpgYGAKCiMjIFBsb3Qgd2l0aCBjb2xvcnMKYGBge3J9CnN2LnByb3QuaW5pdCA9IHJlYWRSRFMocGFzdGUocGF0aC53b3JrLCAnc3ZfcHJvdGVpbnNfbm9fdGVfYmxhc3QucmRzJywgc2VwID0gJycpKQpzdi5wcm90LmluaXQkbmFtZSA9IHNhcHBseShzdi5wcm90LmluaXQkWDEsIGZ1bmN0aW9uKHMpewogIHMgPSBwYXN0ZTAoc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bMToyXSwgY29sbGFwc2UgPSAnfCcpCiAgcmV0dXJuKHMpCn0pCnN2LnByb3QgPSBzdi5wcm90LmluaXRbc3YucHJvdC5pbml0JHByb3QgPT0gMSxdCnN2LnByb3RbLDJdID0gdG9sb3dlcihzdi5wcm90WywyXSkKCnR5cGVzID0gYygnZGlzZWFzZScsICdyZXBlYXQnLCAncmVjZXB0b3InLCAgJ3ppbmMnLCAndHJhbnNjcmlwdGFzZScsICdyZXZlcnNlJywgJ3RyYW5zcG9zJykKZm9yKGkudHlwZSBpbiAxOmxlbmd0aCh0eXBlcykpewogIHN2LnByb3RbLHR5cGVzW2kudHlwZV1dID0gKGdyZXBsKHR5cGVzW2kudHlwZV0sIHN2LnByb3RbLDJdKSkgKiAxCn0Kc3YucHJvdCR0eXBlID0gcm93U3Vtcyhzdi5wcm90Wyx0eXBlc10pCnRhYmxlKHN2LnByb3QkdHlwZSkKCgoKc3YubWVtYiRwcm90ID0gJ25vIHByb3QnCnN2Lm1lbWIkcHJvdFtzdi5tZW1iJG5hbWUgJWluJSBzdi5wcm90LmluaXQkbmFtZV0gPSAndW5kZWZpbmVkIHByb3QnCnN2Lm1lbWIkcHJvdFtzdi5tZW1iJG5hbWUgJWluJSBzdi5wcm90JG5hbWVdID0gJ2RlZmluZWQgcHJvdCcKZm9yKHR5cGUgaW4gdHlwZXMpewogIHN2Lm1lbWIkcHJvdFtzdi5tZW1iJG5hbWUgJWluJSBzdi5wcm90JG5hbWVbc3YucHJvdFssdHlwZV0gPT0gMV1dID0gdHlwZQp9CgpnLm5vZGVzLnByb3QgPSBzdi5tZW1iJHByb3QKZy5ub2Rlcy5wcm90W2cubm9kZXMucHJvdCA9PSAnZGlzZWFzZSddID0gJ2RlZmluZWQgcHJvdCcKbmFtZXMoZy5ub2Rlcy5wcm90KSA9IHN2Lm1lbWIkbmFtZQoKZy5jb2xzID0gZGlzY3JldGVfcmFpbmJvdyhsZW5ndGgodW5pcXVlKHN2Lm1lbWIkcHJvdCkpKQpuYW1lcyhnLmNvbHMpID0gdW5pcXVlKHN2Lm1lbWIkcHJvdCkKIyBuYW1lcyhnLmNvbHMpID0gYygnbm8gcHJvdCcsICJ1bmRlZmluZWQgcHJvdCIsICJyZXZlcnNlIiwgInRyYW5zcG9zIiwicmVwZWF0IiwiemluYyIsICJyZWNlcHRvciIsImRlZmluZWQgcHJvdCIpCgpnLmNvbHNbJ2Rpc2Vhc2UnXSA9ICdibGFjaycKIyBnLmNvbHNbJ2RlZmluZWQgcHJvdCddID0gJyNGMEI4NkUnCiMgZy5jb2xzWydyZXBlYXQnXSA9ICcjQ0FFMEFCJwojIGcuY29sc1snemluayddID0gJyMxQTVEMUEnCiMgZy5jb2xzWydyZWNlcHRvciddID0gJyNFRDdCN0InCgpnLmNvbHNbJ3ppbmMnXSA9ICcjRjQ1MDUwJwpnLmNvbHNbJ3JlcGVhdCddID0gJyNFQTkwNkMnCmcuY29sc1sncmVjZXB0b3InXSA9ICcjRjdEMDYwJwoKZy5jb2xzWydyZXZlcnNlJ10gPSAnIzk4RDhBQScKZy5jb2xzWydkZWZpbmVkIHByb3QnXSA9ICcjN0JBRkRFJwoKCgpzZXQuc2VlZCgyMzkpCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBub2RlLnNpemUgPSAxLAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMucHJvdFtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scywKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKSArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBnLmNvbHMsIAogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoInRyYW5zcG9zIiwicmV2ZXJzZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVwZWF0IiwiemluYyIsInJlY2VwdG9yIiwgImRlZmluZWQgcHJvdCIsICJ1bmRlZmluZWQgcHJvdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibm8gcHJvdCIpLCAKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdQcm90ZWluIGtleS13b3JkOicpICsgdGhlbWUobGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsIDApKQpwID0gcCsgdGhlbWUobGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuNSwgImNtIikpCnAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9uZXdfYWxsLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCiMgCiMgY250ID0gdGFibGUoZy5ub2Rlcy5wcm90KQojIGNudCA9IGMoc3VtKGNudFtjKCJ0cmFuc3BvcyIsInJldmVyc2UiLCJyZXBlYXQiLCJ6aW5jIildKSwgc3VtKGNudFtjKCJyZWNlcHRvciIsImRlZmluZWQgcHJvdCIpXSksCiMgICAgICAgICBjbnRbInVuZGVmaW5lZCBwcm90Il0sIGNudFsibm8gcHJvdCJdKQoKYGBgCgojIyBUeXBlcyBvZiB0aGUgY29tcG9uZW50CgoKYGBge3J9Cgpzdi5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChzdi5lZGdlcyksIGRpcmVjdGVkID0gVCkKc3YuZ3JhcGggPC0gaWdyYXBoOjpzaW1wbGlmeShzdi5ncmFwaCkKc3YuZ3JhcGhjb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyhzdi5ncmFwaCkKCnN2LmNvbXAubWVtYmVyID0gc3YuZ3JhcGhjb21wJG1lbWJlcnNoaXAKCnMudGFncyA9IGMoInRyYW5zcG9zIiwicmV2ZXJzZSIsInJlcGVhdCIsInppbmMiLCAicmVjZXB0b3IiLCJkZWZpbmVkIHByb3QiLCAidW5kZWZpbmVkIHByb3QiLCAnbm8gcHJvdCcpCnMudGFnczAgPSByZXAoJycsIGxlbmd0aChzLnRhZ3MpKQpzLnRhZ3MwWzE6NF0gPSAnVEUtbGlrZScKcy50YWdzMFs1OjZdID0gJ0tub3duIFByb3RlaW5zJwpzLnRhZ3MwWzddID0gJ1VuZGVmLiBQcm90ZWlucycKcy50YWdzMFs4XSA9ICdObyBQcm90ZWlucycKbmFtZXMocy50YWdzMCkgPSBzLnRhZ3MKCmNvbXAudGFncyA9IHJlcCgnJywgbGVuZ3RoKHVuaXF1ZShzdi5jb21wLm1lbWJlcikpKQpmb3Iocy50YWcgaW4gcy50YWdzKXsKICB0bXAudGFncyA9IHVuaXF1ZShzdi5jb21wLm1lbWJlcltuYW1lcyhnLm5vZGVzLnByb3QpW2cubm9kZXMucHJvdCA9PSBzLnRhZ11dKQogIGNvbXAudGFnc1t0bXAudGFnc11bY29tcC50YWdzW3RtcC50YWdzXSA9PSAnJ10gPSBzLnRhZwp9CmNvbXAudGFnc1tjb21wLnRhZ3MgPT0gJyddID0gJ25vIHByb3QnCmNvbXAudGFncyA9IGRhdGEuZnJhbWUodGFibGUoY29tcC50YWdzKSkKY29sbmFtZXMoY29tcC50YWdzKSA9IGMoJ3RhZzEnLCAnZnJlcScpCmNvbXAudGFncyR0YWcxID0gZmFjdG9yKGNvbXAudGFncyR0YWcxLCBsZXZlbHMgPSBzLnRhZ3MpCmNvbXAudGFncyA9IGNvbXAudGFnc1tvcmRlcihjb21wLnRhZ3MkdGFnMSksXQoKY29tcC50YWdzJHRhZzAgPSBzLnRhZ3MwW2NvbXAudGFncyR0YWcxXQpjb21wLnRhZ3MkdGFnMCA9IGZhY3Rvcihjb21wLnRhZ3MkdGFnMCwgbGV2ZWxzID0gdW5pcXVlKHMudGFnczApKQoKeS50aWNrcyA9IHRhcHBseShjb21wLnRhZ3MkZnJlcSwgY29tcC50YWdzJHRhZzAsIHN1bSkKeS50aWNrcyA9IHkudGlja3NbIWlzLm5hKHkudGlja3MpXQoKeXkgPSBzdW0oeS50aWNrcykgLSBjdW1zdW0oeS50aWNrcykgKyB5LnRpY2tzLzIKCmNvbXAudGFncyR5bWluIDwtIGMoMCwgY3Vtc3VtKGNvbXAudGFncyRmcmVxKVstbGVuZ3RoKGNvbXAudGFncyRmcmVxKV0pCmNvbXAudGFncyR5bWF4IDwtIGN1bXN1bShjb21wLnRhZ3MkZnJlcSkKCnguc3RlcCA9IHJlcCgwLCA4KQpuLnN0ZXAgPSAxMAp4LnN0ZXBbYyg1LDcsOCldID0gbi5zdGVwCnguc3RlcCA9IGN1bXN1bSh4LnN0ZXApCgpjb21wLnRhZ3MkeW1pbiA9IGNvbXAudGFncyR5bWluICsgeC5zdGVwCmNvbXAudGFncyR5bWF4ID0gY29tcC50YWdzJHltYXggKyB4LnN0ZXAKCnkubWluID0gdGFwcGx5KGNvbXAudGFncyR5bWluLCBjb21wLnRhZ3MkdGFnMCwgbWluKQp5Lm1heCA9IHRhcHBseShjb21wLnRhZ3MkeW1heCwgY29tcC50YWdzJHRhZzAsIG1heCkKeS52YWwgPSAoeS5tYXggKyB5Lm1pbikgLyAyCnkuY250ID0gdGFwcGx5KGNvbXAudGFncyRmcmVxLCBjb21wLnRhZ3MkdGFnMCwgc3VtKQoKZGYudGV4dCA9IGRhdGEuZnJhbWUoeS5taW4gPSB5Lm1pbiwgeS5tYXggPSB5Lm1heCwgeS52YWwgPSB5LnZhbCwgeS5jbnQgPSB5LmNudCwgbGFiZWwgPSBuYW1lcyh5LnZhbCkpCmRmLnRleHQkYW5nbGVzIDwtIDM2MCAtIChkZi50ZXh0JHkudmFsIC8gKG1heChjb21wLnRhZ3MkeW1heCkgKyBuLnN0ZXApKSAqIDM2MCAKZGYudGV4dCRhbmdsZXNbMjozXSA9IDE4MCArIGRmLnRleHQkYW5nbGVzWzI6M10KCnAgPSBnZ3Bsb3QoY29tcC50YWdzLCBhZXMoeCA9IDAsIHkgPSBmcmVxLCBmaWxsID0gdGFnMSkpICsKICAgZ2VvbV9yZWN0KGFlcyh4bWluID0gLTAuNSwgeG1heCA9IDAuNSwgeW1pbiA9IHltaW4sIHltYXggPSB5bWF4KSkgKwogICBjb29yZF9wb2xhcigieSIsIHN0YXJ0ID0gMCkgKwogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBnLmNvbHMucGx1cykgKyB5bGltKDAsIG1heChjb21wLnRhZ3MkeW1heCkgKyBuLnN0ZXApICsKICAgdGhlbWVfdm9pZCgpICsgeGxpbSgtMS41LCAwLjcpICsgCiAgIGdlb21fdGV4dChkYXRhPWRmLnRleHQsIGFlcyh4ID0gMC43LCB5ID0geS52YWwsIGxhYmVsID0gcGFzdGUobGFiZWwsIHkuY250LCBzZXAgPSAnOiAnKSksIAogICAgICAgICAgICAgYW5nbGUgPSBkZi50ZXh0JGFuZ2xlcywgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gLTEuNSwgeSA9IDAsIGxhYmVsID0gcGFzdGUoJ1RvdGFsJyxzdW0oY29tcC50YWdzJGZyZXEpLCdcbiBjb25uZWN0ZWQgXG5jb21wb25lbnRzJykpIAoKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX25ld19waWVfY2hhcnQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDMuMSwgaGVpZ2h0ID0gMy4xKQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKCgojIyMjIEkgZG9uJ3Qga25vdwpgYGB7cn0Kc3Yuc2UkZnJlcSA9IHN2LnNlJGZyZXEubWF4Cm4uY3V0b2ZmID0gMwpuID0gMjgKc3Yuc2Ukc2luID0gJ2luZGVsJwpzdi5zZSRzaW5bc3Yuc2UkZnJlcSA+PSAobiAtIG4uY3V0b2ZmKV0gPSAnZGVsZXRpb24nCnN2LnNlJHNpbltzdi5zZSRmcmVxIDw9IG4uY3V0b2ZmXSA9ICdpbnNlcnRpb24nCgoKZy5ub2Rlcy5wcm90LnNpbiA9IGcubm9kZXMucHJvdApnLm5vZGVzLnByb3Quc2luW25hbWVzKGcubm9kZXMucHJvdC5zaW4pICVpbiUgc3Yuc2UkbmFtZVtzdi5zZSRzaW4gIT0gJ2luc2VydGlvbiddIF0gPSAnbmEnCmcuY29sc1snbmEnXSA9ICd3aGl0ZScKCgoKCnNldC5zZWVkKDIzOSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90LnNpbltiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scywKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKQpwIAoKIyAKIyBwYXRoLmZpZ3VyZXMgID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya190ZS8nCiMgcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3N2X25vdGVfaW5zZXJ0aW9uLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQojIHByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCiMgZGV2Lm9mZigpCgoKYWxwaGEuZWR0YSA9IHJlcCgxLCBsZW5ndGgoYi5ncmFwaC5uYW1lcykpCm5hbWVzKGFscGhhLmVkdGEpID0gYi5ncmFwaC5uYW1lcwoKc3YuYW5ub3QuYWR0YSA9IHJvd1N1bXMoc3YuYW5ub3RbLDExOm5jb2woc3YuYW5ub3QpXSA+IDAuNykgPiAwCnN2LmFubm90LmFkdGEgPSBzdi5hbm5vdC5hZHRhW3N2LnNlJGdyXQpuYW1lcyhzdi5hbm5vdC5hZHRhKSA9IHN2LnNlJG5hbWUKc3YuYW5ub3QuYWR0YSA9IHN2LmFubm90LmFkdGFbc3YuYW5ub3QuYWR0YV0KYWxwaGEuZWR0YVtuYW1lcyhhbHBoYS5lZHRhKSAlaW4lIG5hbWVzKHN2LmFubm90LmFkdGEpXSA9IDAKCgpzZXQuc2VlZCgyMzkpCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBub2RlLnNpemUgPSAxLAogICAgICAgICAgICBhbHBoYT0xLWFscGhhLmVkdGEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9ub3RlX2VkdGEucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGF0aC5maWd1cmVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvJwpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX25vdGVfZWR0YV9ub19sZWdlbmQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKIyMgUGxvdCB3aXRoIGNvbXBvbmVudCBJRApgYGB7cn0KCgp0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoc3YuZWRnZXMpLCBkaXJlY3RlZCA9IFQpCnRtcC5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRtcC5ncmFwaCkKdG1wLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRtcC5ncmFwaCkKCnNpemUubGltaXQgPSA1CmNvbXAuaWQgPSBhcy5jaGFyYWN0ZXIodG1wLmNvbXAkbWVtYmVyc2hpcCkKbmFtZXMoY29tcC5pZCkgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKQpjb21wLmlkW3RtcC5jb21wJGNzaXplW3RtcC5jb21wJG1lbWJlcnNoaXBdIDwgc2l6ZS5saW1pdF0gPSAnJwoKbmFtZXMudGUgPSBuYW1lcyhnLm5vZGVzLnByb3QpW2cubm9kZXMucHJvdCAlaW4lIGMoJ3RyYW5zcG9zJywgJ3JldmVyc2UnKV0KCmNvbXAuaWRbIShuYW1lcyhjb21wLmlkKSAlaW4lIG5hbWVzLnRlKV0gPSAnJwoKY29tcC5pZFtkdXBsaWNhdGVkKGNvbXAuaWQpXSA9ICcnCgoKY29tcC5yZW1haW4gPSBhcy5udW1lcmljKGNvbXAuaWRbY29tcC5pZCAhPSAnJ10pCmFscGhhID0gcmVwKDAsIGxlbmd0aChiLmdyYXBoLm5hbWVzKSkKbmFtZXMoYWxwaGEpID0gbmFtZXModG1wLmNvbXAkbWVtYmVyc2hpcCkKYWxwaGFbdG1wLmNvbXAkbWVtYmVyc2hpcCAlaW4lIGNvbXAucmVtYWluXSA9IDEKCnNldC5zZWVkKDIzOSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IGNvbXAuaWRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBsYWJlbC5jb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgIGxhYmVsLnNpemUgPSAzLAogICAgICAgICAgICBlZGdlLmNvbG9yID0gImdyZXkiLCAKICAgICAgICAgICAgYWxwaGEgPSBhbHBoYVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgoKcGF0aC5maWd1cmVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvJwpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfc3Zfbm90ZV9udW1iZXJzLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKCiMgT3JkZXIgb2YgY29tcG9uZW50cwpjbnQgPSB0YWJsZSh0bXAuY29tcCRtZW1iZXJzaGlwW3RtcC5jb21wJG1lbWJlcnNoaXAgJWluJSBjb21wLnJlbWFpbl0pCmNudCA9IGNudFtvcmRlcigtY250KV0KCmBgYAoKIyMgQ05WCmBgYHtyfQoKY252ID0gcmVhZFJEUygnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3N2L3NpbWlsYXJfY252X3N2X29uX2FjY2Vzc2lvbnNfY3VtXzAuOS5yZHMnKQoKYGBgCgojIyBQbG90IG9uZSBzcGVjaWZpYyBuZXR3b3JrCmBgYHtyfQoKcGF0aC5maWd1cmVzLmV4YW1wbGVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvZXhhbXBsZXMvJwoKIyAKIyB0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoc3YuZWRnZXMpLCBkaXJlY3RlZCA9IFQpCiMgdG1wLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodG1wLmdyYXBoKQojIHRtcC5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0bXAuZ3JhcGgpCiMgCiMgdG1wLmNudCA9IHRhYmxlKHRtcC5jb21wJG1lbWJlcnNoaXApCiMgdG1wLmNudCA9IC1zb3J0KC10bXAuY250KQoKdG1wLmNudCA9IGNudAoKZm9yKGsgaW4gMTpsZW5ndGgodG1wLmNudCkpewogIHRtcC5rID0gYXMubnVtZXJpYyhuYW1lcyh0bXAuY250KVtrXSkKICB0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwID09IHRtcC5rXQogIGIuZ3JhcGguc3ViID0gc3YuZWRnZXNbKHN2LmVkZ2VzWywxXSAlaW4lIHRtcC5uYW1lcykgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAoc3YuZWRnZXNbLDJdICVpbiUgdG1wLm5hbWVzKSxdCiAgCiAgCiAgZy5wYXJ0LnN1YiA8LSBuZXR3b3JrKGIuZ3JhcGguc3ViLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKICBiLmdyYXBoLm5hbWVzLnN1YiA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydC5zdWIpCiAgCiAgCiAgICAKICBiLmdyYXBoLnNpemUuc3ViIDwtIGFzLm51bWVyaWMoc3ViKCIuKlxcfCIsICIiLCBiLmdyYXBoLm5hbWVzLnN1YikpCiAgbmFtZXMoYi5ncmFwaC5zaXplLnN1YikgPSBiLmdyYXBoLm5hbWVzLnN1YgogICMgYi5ncmFwaC5zaXplLnN1YiA9IGNlaWxpbmcobG9nKGIuZ3JhcGguc2l6ZS5zdWIsIDEwKSkKICAKICBpZigobGVuZ3RoKHVuaXF1ZSggZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXMuc3ViXSkpID09IDEpKXsKICAgIHNldC5zZWVkKDIwKQogICAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1YiwgbGFiZWwgPSBiLmdyYXBoLnNpemUuc3ViW2IuZ3JhcGgubmFtZXMuc3ViXSwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgbm9kZS5zaXplID0gMTUsCiAgICAgICAgICAgICAgICBhcnJvdy5nYXAgPSAwLjA3LCBhcnJvdy5zaXplID0gMywKICAgICAgICAgICAgICAgIGNvbG9yID0gZy5jb2xzW2cubm9kZXMucHJvdFtiLmdyYXBoLm5hbWVzLnN1Yl1bMV1dLAogICAgICAgICAgICAgICAgKSArIGd1aWRlcyhzaXplID0gRikgKyAgZ2d0aXRsZShwYXN0ZSgnQ29tcG9uZW50ICMnLCB0bXAuaykpCiAgICBwCiAgfSBlbHNlIHsKICAgIHNldC5zZWVkKDIwKQogICAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1YiwgbGFiZWwgPSBiLmdyYXBoLnNpemUuc3ViW2IuZ3JhcGgubmFtZXMuc3ViXSwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgbm9kZS5zaXplID0gMTUsCiAgICAgICAgICAgICAgICBhcnJvdy5nYXAgPSAwLjA3LCBhcnJvdy5zaXplID0gMywKICAgICAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXMuc3ViXSwKICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBnLmNvbHMsCiAgICAgICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKSArICBnZ3RpdGxlKHBhc3RlKCdDb21wb25lbnQgIycsIHRtcC5rKSkKICAgIHAKICB9CiAgCiAKICAKICBwZGYocGFzdGUocGF0aC5maWd1cmVzLmV4YW1wbGVzLCAnZ3JhcGhfc3ZfZXhhbXBsZV8nLGssJ19jb21wXycsdG1wLmssJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKICBwcmludChwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKICBkZXYub2ZmKCkKICAKICAjIGFubm90YXRpb24KICBhbm5vdC50bXAgPSBzdi5wcm90W3N2LnByb3QkbmFtZSAlaW4lIGIuZ3JhcGgubmFtZXMuc3ViLF0KICAjIGFubm90LnRtcCA9IGFubm90LnRtcFthbm5vdC50bXAkdHJhbnNwb3MgPT0gMSxdCiAgCiAgd3JpdGUudGFibGUoYW5ub3QudG1wLCBwYXN0ZShwYXRoLmZpZ3VyZXMuZXhhbXBsZXMsICdncmFwaF9zdl9leGFtcGxlXycsaywnX3BibGFzdC50eHQnLCBzZXAgPSAnJyksIAogICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IEYsIHF1b3RlID0gRiwgc2VwID0gJ1x0JykKICAKICAKICAjIGlmIEVEVEEgYW5ub3RhdGlvbiBleGlzdHMKICBzdi50bXAgPSB1bmlxdWUoYyhiLmdyYXBoLnN1YikpCiAgc3YudG1wLmN1dCA8LSBnc3ViKCJcXHwuKiIsICIiLCBzdi50bXApCiAgc3YuYW5ub3QudG1wID0gc3YuYW5ub3Rbc3YudG1wLmN1dCxdCiAgbi5maXggPSA5CiAgc3YuYW5ub3QudG1wICA9IHN2LmFubm90LnRtcFssYygxOm4uZml4LG4uZml4K3doaWNoKGNvbFN1bXMoc3YuYW5ub3QudG1wWywobi5maXgrMSk6bmNvbChzdi5hbm5vdC50bXApXSkgIT0gMCkpXQogIHJvd25hbWVzKHN2LmFubm90LnRtcCkgPSBzdi50bXAKICAgIAogIHdyaXRlLnRhYmxlKHN2LmFubm90LnRtcCwgcGFzdGUocGF0aC5maWd1cmVzLmV4YW1wbGVzLCAnZ3JhcGhfc3ZfZXhhbXBsZV8nLGssJ19lZHRhLnR4dCcsIHNlcCA9ICcnKSwgCiAgICAgICAgICAgICByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYsIHNlcCA9ICdcdCcpCiAgCiAgIyBDb3B5ME51bWJlciB2YXJpYXRpb24KICBjbnYudG1wID0gY252W3N2LnRtcCxdCiAgCiAgaGVhdG1hcChjbnYudG1wLCBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwgInJlZCIpKSgyMCkpCiAgCn0KCmBgYAojIFBpZS1jaGFydCBvZiBwcm90ZWlucwpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKZGF0YSA8LSBkYXRhLmZyYW1lKAogIHR5cGUgPSBjKCJubyBwcm90ZWlucyIsICJURS1yZWxhdGVkIiwgItCa0LDRgtC10LPQvtGA0LjRjyAyIiwgItCa0LDRgtC10LPQvtGA0LjRjyAzIiwgItCa0LDRgtC10LPQvtGA0LjRjyA0IiksCiAgdmFsdWUgPSBjKDEzNSwgNjMsIDg1LCAxMzMpCikKCnBpZS5jaGFydCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSAiIiwgeSA9IHZhbHVlLCBmaWxsID0gdHlwZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsKICB0aGVtZV92b2lkKCkKCnBpZS5jaGFydAoKYGBgCgojIyBBZG1peHR1cmUgZ3JvdXBzCmBgYHtyfQpncm91cHMgPC0gYygKICAiZ2VybWFueSIsCiAgInNvdXRoX3N3ZWRlbiIsCiAgIm5vcnRoX3N3ZWRlbiIsCiAgInNvdXRoX3N3ZWRlbiIsCiAgIm5vcnRoX3N3ZWRlbiIsCiAgImdlcm1hbnkiLAogICJ3ZXN0ZXJuX2V1cm9wZSIsCiAgImNlbnRyYWxfZXVyb3BlIiwKICAiaXRhbHlfYmFsa2FuX2NhdWNhc3VzIiwKICAic3BhaW4iLAogICJyZWxpY3QiLAogICJhc2lhIiwKICAiY2VudHJhbF9ldXJvcGUiLAogICJhZG1peGVkIiwKICAic3BhaW4iLAogICJyZWxpY3QiLAogICJpdGFseV9iYWxrYW5fY2F1Y2FzdXMiLAogICJ3ZXN0ZXJuX2V1cm9wZSIsCiAgImFzaWEiLAogICJhZnJpY2EiLAogICJjaGluYSIsCiAgImNoaW5hIiwKICAiYWZyaWNhIiwKICAiYWZyaWNhIiwKICAibWFkZWlyYSIsCiAgIm1hZGVpcmEiLAogICJhZnJpY2EiCikKCiMg0JjRgdC/0L7Qu9GM0LfRg9C10Lwg0YTRg9C90LrRhtC40Y4gdGFibGUoKSDQtNC70Y8g0L/QvtC00YHRh9C10YLQsCDQutC+0LvQuNGH0LXRgdGC0LLQsCDRjdC70LXQvNC10L3RgtC+0LIg0LIg0LrQsNC20LTQvtC5INCz0YDRg9C/0L/QtQphcy5tYXRyaXgodGFibGUoZ3JvdXBzKSkKYGBgCgoKCiMgT0xECmBgYHtyfQpzdW5zZXQgPC0gY29sb3VyKCJzdW5zZXQiKQpkaXNjcmV0ZV9yYWluYm93IDwtIGNvbG91cigiZGlzY3JldGUgcmFpbmJvdyIpCgpmaWxlLnRlID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29yay9ibGFzdF90ZXNfYW5uLnR4dCcKc2ltLmN1dG9mZiA9IDAuODUKbGVuLmN1dG9mZiA9IDEwMApgYGAKCgpgYGB7cn0KCmIgPSByZWFkLnRhYmxlKGZpbGUudGUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpiID0gYltiJFYxICE9IGIkVjgsXQpiJGxlbjEgPSBhcy5udW1lcmljKHNhcHBseShiJFYxLCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs3XSkpCmIkbGVuMiA9IGFzLm51bWVyaWMoc2FwcGx5KGIkVjgsIGZ1bmN0aW9uKHMpIHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzddKSkKYiA9IGJbYiRsZW4xID49IGxlbi5jdXRvZmYsXQpiID0gYltiJGxlbjIgPj0gbGVuLmN1dG9mZixdCmIkY29tYiA9IHBhc3RlKGIkVjEsIGIkVjgsIHNlcCA9ICdeJykKCiMgT3JkZXIgcG9zaXRpb25zIGluIGJhc2UKaWR4ID0gYiRWNCA+IGIkVjUKdG1wID0gYltpZHgsICdWNCddCmJbaWR4LCAnVjQnXSA9IGJbaWR4LCAnVjUnXQpiW2lkeCwgJ1Y1J10gPSB0bXAKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBHZXQgc2VwYXJhdGVseSB0aG9zZSwgd2hvIGhhcyBhIHVuaXF1ZSBjb3ZlcmFnZQpjb21iLnRibCA9IHRhYmxlKGIkY29tYikKaWR4LnVuaSA9IGIkY29tYiAlaW4lIG5hbWVzKGNvbWIudGJsKVtjb21iLnRibCA9PSAxXQpiLnVuaSA9IGJbaWR4LnVuaSxdCmIgPSBiWyFpZHgudW5pLF0KCiMgVGhpcyB2YXJpYWJsZSB3aWxsIGJlIHVzZWQgbGF0ZXIKYi51bmkkcDEgPSAoYi51bmkkVjMgLSBiLnVuaSRWMiArIDEpIC8gYi51bmkkbGVuMQpiLnVuaSRwMiA9IChiLnVuaSRWNSAtIGIudW5pJFY0ICsgMSkgLyBiLnVuaSRsZW4yCmIudW5pID0gYi51bmlbKGIudW5pJHAxID49IHNpbS5jdXRvZmYpIHwgKGIudW5pJHAyID49IHNpbS5jdXRvZmYpLF0KCmIucmVsYXRpb25zID0gZGF0YS5mcmFtZShzdWIudGUgPSBiLnVuaSRWMVtiLnVuaSRwMSA+PSBzaW0uY3V0b2ZmXSwKICAgICAgICAgICAgICAgICAgICAgICAgIHRlID0gYi51bmkkVjhbYi51bmkkcDEgPj0gc2ltLmN1dG9mZl0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpiLnJlbGF0aW9ucyA9IHJiaW5kKGIucmVsYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoc3ViLnRlID0gYi51bmkkVjhbYi51bmkkcDIgPj0gc2ltLmN1dG9mZl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZSA9IGIudW5pJFYxW2IudW5pJHAyID49IHNpbS5jdXRvZmZdLCBzdHJpbmdzQXNGYWN0b3JzID0gRikpCmIucmVsYXRpb25zID0gdW5pcXVlKGIucmVsYXRpb25zKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIE1pbi1tYXggb2YgdGhlIGNvdmVyYWdlIHRvIHJlbW92ZSB0aG9zZSwgd2hvIGFyZSBOT1QgaW4gZWFjaCBvdGhlciBjb21wbGV0ZWx5CmIuY292ID0gdGFwcGx5KGIkVjIsIGIkY29tYiwgbWluKQpiLmNvdiA9IGRhdGEuZnJhbWUoY29tYiA9IG5hbWVzKGIuY292KSwgVjIgPSBiLmNvdikKYi5jb3YkVjMgPSB0YXBwbHkoYiRWMywgYiRjb21iLCBtYXgpCmIuY292JFY0ID0gdGFwcGx5KGIkVjQsIGIkY29tYiwgbWluKQpiLmNvdiRWNSA9IHRhcHBseShiJFY1LCBiJGNvbWIsIG1heCkKYi5jb3YkbGVuMSA9IHRhcHBseShiJGxlbjEsIGIkY29tYiwgdW5pcXVlKQpiLmNvdiRsZW4yID0gdGFwcGx5KGIkbGVuMiwgYiRjb21iLCB1bmlxdWUpCmIuY292JHAxID0gKGIuY292JFYzIC0gYi5jb3YkVjIgKyAxKSAvIGIuY292JGxlbjEKYi5jb3YkcDIgPSAoYi5jb3YkVjUgLSBiLmNvdiRWNCArIDEpIC8gYi5jb3YkbGVuMgoKY29tYi51bmNvdiA9IGIuY292JGNvbWJbKGIuY292JHAxIDwgc2ltLmN1dG9mZikgJiAoYi5jb3YkcDIgPCBzaW0uY3V0b2ZmKV0KCmIgPSBiWyEoYiRjb21iICVpbiUgY29tYi51bmNvdiksXQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENhbGN1bGF0ZSB0aGUgY292ZXJhZ2UgZGlyZWN0bHkgZm9yIHRoZSBmaXJzdApiID0gYltvcmRlcihiJFYzKSxdCmIgPSBiW29yZGVyKGIkVjIpLF0KYiA9IGJbb3JkZXIoYiRjb21iKSxdCgojIFJlbW92ZSBuZXN0ZWQKaWR4ID0gd2hpY2goKGIkVjNbLW5yb3coYildID4gYiRWM1stMV0pICYgKGIkY29tYlstbnJvdyhiKV0gPT0gYiRjb21iWy0xXSkpICsgMQpiMSA9IGJbLWlkeCxdCgojIENvbXB1dGUgZ2FwcwpiMSRnYXAgPSBjKGIxJFYyWy0xXSAtIGIxJFYzWy1ucm93KGIxKV0gLSAxLCAwKQpiMSRnYXBbYjEkZ2FwIDwgMF0gPSAwCmlkeC5kaWZmLmNvbWIgPSB3aGljaChiMSRjb21iWy0xXSAhPSBiMSRjb21iWy1ucm93KGIxKV0pCmIxJGdhcFtpZHguZGlmZi5jb21iXSA9IDAKCmIuY292ID0gdGFwcGx5KGIxJFYyLCBiMSRjb21iLCBtaW4pCmIuY292ID0gZGF0YS5mcmFtZShjb21iID0gbmFtZXMoYi5jb3YpLCBWMiA9IGIuY292KQpiLmNvdiRWMyA9IHRhcHBseShiMSRWMywgYjEkY29tYiwgbWF4KQpiLmNvdiRsZW4xID0gdGFwcGx5KGIxJGxlbjEsIGIxJGNvbWIsIHVuaXF1ZSkKYi5jb3YkZ2FwID0gdGFwcGx5KGIxJGdhcCwgYjEkY29tYiwgc3VtKQpiLmNvdiRsZW4xID0gYi5jb3YkbGVuMSAKYi5jb3YkcDEgPSAoYi5jb3YkVjMgLSBiLmNvdiRWMiArIDEgLSBiLmNvdiRnYXApIC8gYi5jb3YkbGVuMQpiLmNvdiRWMSA9IHRhcHBseShiMSRWMSwgYjEkY29tYiwgdW5pcXVlKQpiLmNvdiRWOCA9IHRhcHBseShiMSRWOCwgYjEkY29tYiwgdW5pcXVlKQoKYi5jb3YgPSBiLmNvdltiLmNvdiRwMSA+PSBzaW0uY3V0b2ZmLF0KCgpiLnJlbGF0aW9ucyA9IHJiaW5kKGIucmVsYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoc3ViLnRlID0gYi5jb3YkVjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZSA9IGIuY292JFY4LCBzdHJpbmdzQXNGYWN0b3JzID0gRikpCgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENhbGN1bGF0ZSB0aGUgY292ZXJhZ2UgZGlyZWN0bHkgZm9yIHRoZSBzZWNvbmQKYiA9IGJbb3JkZXIoYiRWNSksXQpiID0gYltvcmRlcihiJFY0KSxdCmIgPSBiW29yZGVyKGIkY29tYiksXQoKIyBSZW1vdmUgbmVzdGVkCmlkeCA9IHdoaWNoKChiJFY1Wy1ucm93KGIpXSA+IGIkVjVbLTFdKSAmIChiJGNvbWJbLW5yb3coYildID09IGIkY29tYlstMV0pKSArIDEKYjEgPSBiWy1pZHgsXQoKIyBDb21wdXRlIGdhcHMKYjEkZ2FwID0gYyhiMSRWNFstMV0gLSBiMSRWNVstbnJvdyhiMSldIC0gMSwgMCkKYjEkZ2FwW2IxJGdhcCA8IDBdID0gMAppZHguZGlmZi5jb21iID0gd2hpY2goYjEkY29tYlstMV0gIT0gYjEkY29tYlstbnJvdyhiMSldKQpiMSRnYXBbaWR4LmRpZmYuY29tYl0gPSAwCgpiLmNvdiA9IHRhcHBseShiMSRWNCwgYjEkY29tYiwgbWluKQpiLmNvdiA9IGRhdGEuZnJhbWUoY29tYiA9IG5hbWVzKGIuY292KSwgVjQgPSBiLmNvdikKYi5jb3YkVjUgPSB0YXBwbHkoYjEkVjUsIGIxJGNvbWIsIG1heCkKYi5jb3YkbGVuMiA9IHRhcHBseShiMSRsZW4yLCBiMSRjb21iLCB1bmlxdWUpCmIuY292JGdhcCA9IHRhcHBseShiMSRnYXAsIGIxJGNvbWIsIHN1bSkKYi5jb3YkbGVuMiA9IGIuY292JGxlbjIgCmIuY292JHAxID0gKGIuY292JFY1IC0gYi5jb3YkVjQgKyAxIC0gYi5jb3YkZ2FwKSAvIGIuY292JGxlbjIKYi5jb3YkVjEgPSB0YXBwbHkoYjEkVjEsIGIxJGNvbWIsIHVuaXF1ZSkKYi5jb3YkVjggPSB0YXBwbHkoYjEkVjgsIGIxJGNvbWIsIHVuaXF1ZSkKCmIuY292ID0gYi5jb3ZbYi5jb3YkcDEgPj0gc2ltLmN1dG9mZixdCgoKYi5yZWxhdGlvbnMgPSByYmluZChiLnJlbGF0aW9ucywKICAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKHN1Yi50ZSA9IGIuY292JFY4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGUgPSBiLmNvdiRWMSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpKQoKICAKYi5yZWxhdGlvbnMgPSB1bmlxdWUoYi5yZWxhdGlvbnMpCgoKYi5yZWxhdGlvbnMKCmBgYAoKCiMgRGVmaW5lIGNsdXN0ZXJzCmBgYHtyfQpiLm5vZGVzID0gcmJpbmQoYi5yZWxhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShzdWIudGUgPSBiLnJlbGF0aW9ucyR0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlID0gYi5yZWxhdGlvbnMkc3ViLnRlKSkKCmIubm9kZXMkY29tYiA9IHBhc3RlKGIubm9kZXMkc3ViLnRlLCBiLm5vZGVzJHRlLCBzZXAgPSAnXicpCgpjb21iLnRibCA9IHRhYmxlKGIubm9kZXMkY29tYikKY29tYi5iYWNrLmFuZC5mb3RoID0gbmFtZXMoY29tYi50YmwpW2NvbWIudGJsID49IDJdCmIubm9kZXMgPSBiLm5vZGVzW2Iubm9kZXMkY29tYiAlaW4lIGNvbWIuYmFjay5hbmQuZm90aCxdCmIubm9kZXMgPSB1bmlxdWUoYi5ub2Rlc1ssIGMoJ3N1Yi50ZScsICd0ZScpXSkKCgp0ZS5ub2RlcyA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChiLm5vZGVzKSwgZGlyZWN0ZWQgPSBUKQp0ZS5ub2RlcyA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLm5vZGVzKQp0ZS5ub2Rlcy5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0ZS5ub2RlcykKCm5vZGVzID0gcGFzdGUoJ04nLCB0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXAsIHNlcCA9ICcnKQpuYW1lcyhub2RlcykgPSBuYW1lcyh0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXApCmBgYAoKIyMgSWRlbnRpZnkgZmFtaWx5IGZvciBlYWNoIG5vZGUKYGBge3J9Cgpub2Rlcy5mYW1pbHkgPSBzYXBwbHkobmFtZXMobm9kZXMpLCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs2XSkKCm5vZGVzLmZhbWlseS5tYXggPSB0YXBwbHkobm9kZXMuZmFtaWx5LCBub2RlcywgZnVuY3Rpb24ocyl7CiAgdGJsID0gdGFibGUocykKICBmID0gbmFtZXModGJsKVt0YmwgPT0gbWF4KHRibCldCiAgaWYobGVuZ3RoKGYpID09IDEpewogICAgcmV0dXJuKGYpCiAgfSBlbHNlIHsKICAgIHJldHVybignTWl4JykKICB9Cn0pCgpub2Rlcy5mYW1pbHkubWF4W25vZGVzLmZhbWlseS5tYXggJWluJSBjKCdETkEvUG9nbycsICdETkEvVGMxJywgJ0ROQS9IYXJiaW5nZXInLCAnRE5BL0VuLVNwbScsCiAgICAgICAgICAgICAgICAgICAgICdETkEvSEFUJywgJ0ROQScsICdETkEvTWFyaW5lcicpXSA9ICdETkEnCm5vZGVzLmZhbWlseS5tYXhbbm9kZXMuZmFtaWx5Lm1heCAlaW4lIGMoJ1JhdGhFMV9jb25zJywgJ1JhdGhFMl9jb25zJyldID0gJ0ROQScKbm9kZXMuZmFtaWx5Lm1heFtub2Rlcy5mYW1pbHkubWF4ICVpbiUgYygnTElORS9MMScsICdMSU5FPycpXSA9ICdMSU5FJwpub2Rlcy5mYW1pbHkubWF4W25vZGVzLmZhbWlseS5tYXggJWluJSBjKCdVbmFzc2lnbmVkJyldID0gJ01peCcKbm9kZXMuZmFtaWx5LnVuaXF1ZSA9IHVuaXF1ZShub2Rlcy5mYW1pbHkubWF4KQoKCgpgYGAKCgojIyBHcmFwaCB3aXRob3V0IHNpbmdsZXRvbnMKYGBge3J9CgpiLmdyYXBoLmluaXQgPSBiLnJlbGF0aW9uc1soYi5yZWxhdGlvbnMkc3ViLnRlICVpbiUgbmFtZXMobm9kZXMpKSAmIChiLnJlbGF0aW9ucyR0ZSAlaW4lIG5hbWVzKG5vZGVzKSksXQpiLmdyYXBoID0gYi5ncmFwaC5pbml0CmIuZ3JhcGggPSBjYmluZChub2Rlc1thcy5jaGFyYWN0ZXIoYi5ncmFwaCRzdWIudGUpXSwgbm9kZXNbYXMuY2hhcmFjdGVyKGIuZ3JhcGgkdGUpXSkKYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoKQoKCmIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KCiMgcmVkdWNlIGluZGlyZWN0IGFycm93cwppZHgucmVtb3ZlID0gYygpCmZvcihpLmVkZ2UgaW4gMTpucm93KGIuZ3JhcGgpKXsKICBpZihpLmVkZ2UgJSUgMTAwMCA9PSAwKSBwcmludChpLmVkZ2UpCiAgdG1wLnRvID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoW2kuZWRnZSwxXSwyXQogIHRtcC5mcm9tID0gYi5ncmFwaFtiLmdyYXBoWywyXSA9PSBiLmdyYXBoW2kuZWRnZSwyXSwxXQogIGlmKGxlbmd0aChpbnRlcnNlY3QodG1wLnRvLCB0bXAuZnJvbSkpID4gMCkgaWR4LnJlbW92ZSA9IGMoaWR4LnJlbW92ZSwgaS5lZGdlKQp9CmlkeC5yZW1vdmUgPSB1bmlxdWUoaWR4LnJlbW92ZSkKYi5ncmFwaCA9IGIuZ3JhcGhbLWlkeC5yZW1vdmUsXQoKCiMgdGUuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKIyB0ZS5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLmdyYXBoKQojIHRlLmdyYXBoLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLmdyYXBoKQoKCm5vZGVzLmZhbWlseS5tYXguZ3JhcGggPSBub2Rlcy5mYW1pbHkubWF4W25hbWVzKG5vZGVzLmZhbWlseS5tYXgpICVpbiUgdW5pcXVlKGMoYi5ncmFwaFssMV0sIGIuZ3JhcGhbLDJdKSldCgpncmFwaC5jb2xzID0gc3Vuc2V0KGxlbmd0aCh1bmlxdWUobm9kZXMuZmFtaWx5Lm1heC5ncmFwaCkpKQoKZ3JhcGguY29scyA9IGRpc2NyZXRlX3JhaW5ib3cobGVuZ3RoKHVuaXF1ZShub2Rlcy5mYW1pbHkubWF4LmdyYXBoKSkpCm5hbWVzKGdyYXBoLmNvbHMpID0gdW5pcXVlKG5vZGVzLmZhbWlseS5tYXguZ3JhcGgpCmcucGFydCA8LSBuZXR3b3JrKGIuZ3JhcGgsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRkFMU0UsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCBub2RlLnNpemUgPSAxLCAKICAgICAgICAgICAgY29sb3IgPSBub2Rlcy5mYW1pbHkubWF4LmdyYXBoLCBwYWxldHRlID0gZ3JhcGguY29scywKICAgICAgICAgICAgbW9kZSA9ICJrYW1hZGFrYXdhaSIpIyArIGd1aWRlcyhzaXplID0gRkFMU0UpCnAKCmBgYAojIyBHcmFwaCBXSVRIIHNpbmdsZXRvbnMKYGBge3J9CgoKbmFtZXMuY29yZSA9IG5hbWVzKG5vZGVzLmZhbWlseS5tYXguZ3JhcGgpCgpiLmdyYXBoLmluaXQgPSBiLnJlbGF0aW9ucwpmb3IoaSBpbiAxOjIpewogIGIuZ3JhcGguaW5pdFtiLmdyYXBoLmluaXRbLGldICVpbiUgbmFtZXMobm9kZXMpLCBpXSA9IG5vZGVzW2IuZ3JhcGguaW5pdFtiLmdyYXBoLmluaXRbLGldICVpbiUgbmFtZXMobm9kZXMpLCBpXV0KfQoKYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoLmluaXQpCmIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoKQojIFZlcnRlY2VzIGZyb20gdGhlIHByZXZpb3VzIGdyYXBoCmIuZ3JhcGggPSBiLmdyYXBoWyhiLmdyYXBoWywxXSAlaW4lIG5hbWVzLmNvcmUpIHwgKGIuZ3JhcGhbLDJdICVpbiUgbmFtZXMuY29yZSksXQoKCiMgcmVkdWNlIGluZGlyZWN0IGFycm93cwppZHgucmVtb3ZlID0gYygpCmZvcihpLmVkZ2UgaW4gMTpucm93KGIuZ3JhcGgpKXsKICBpZihpLmVkZ2UgJSUgMTAwMCA9PSAwKSBwcmludChpLmVkZ2UpCiAgdG1wLnRvID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoW2kuZWRnZSwxXSwyXQogIHRtcC5mcm9tID0gYi5ncmFwaFtiLmdyYXBoWywyXSA9PSBiLmdyYXBoW2kuZWRnZSwyXSwxXQogIGlmKGxlbmd0aChpbnRlcnNlY3QodG1wLnRvLCB0bXAuZnJvbSkpID4gMCkgaWR4LnJlbW92ZSA9IGMoaWR4LnJlbW92ZSwgaS5lZGdlKQp9CmlkeC5yZW1vdmUgPSB1bmlxdWUoaWR4LnJlbW92ZSkKYi5ncmFwaCA9IGIuZ3JhcGhbLWlkeC5yZW1vdmUsXQoKdGUuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKZCA8LSBpZ3JhcGg6OmRpc3RhbmNlcyh0ZS5ncmFwaCkKIyB0ZS5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLmdyYXBoKQojIHRlLmdyYXBoLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLmdyYXBoKQoKbmFtZXMubmV3ID0gdW5pcXVlKHNldGRpZmYoYyhiLmdyYXBoWywxXSwgYi5ncmFwaFssMl0pLCBuYW1lcyhub2Rlcy5mYW1pbHkubWF4KSkpCiMgbmFtZXMubmV3LnZhbCA9IHBhc3RlKCdHJywxOmxlbmd0aChuYW1lcy5uZXcpLCBzZXAgPSAnJykKIyBuYW1lcyhuYW1lcy5uZXcudmFsKSA9IG5hbWVzLm5ldwojIG5hbWVzLm5ldy52YWwgPSAKCm5hbWVzLm5ldy5mYW1pbHkgPSBzYXBwbHkobmFtZXMubmV3LCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs2XSkKbmFtZXMubmV3LmZhbWlseVtuYW1lcy5uZXcuZmFtaWx5ICVpbiUgYygnRE5BL1BvZ28nLCAnRE5BL1RjMScsICdETkEvSGFyYmluZ2VyJywgJ0ROQS9Fbi1TcG0nLAogICAgICAgICAgICAgICAgICAgICAnRE5BL0hBVCcsICdETkEnLCAnRE5BL01hcmluZXInKV0gPSAnRE5BJwpuYW1lcy5uZXcuZmFtaWx5W25hbWVzLm5ldy5mYW1pbHkgJWluJSBjKCdSYXRoRTFfY29ucycsICdSYXRoRTJfY29ucycpXSA9ICdETkEnCm5hbWVzLm5ldy5mYW1pbHlbbmFtZXMubmV3LmZhbWlseSAlaW4lIGMoJ0xJTkUvTDEnLCAnTElORT8nKV0gPSAnTElORScKbmFtZXMubmV3LmZhbWlseVtuYW1lcy5uZXcuZmFtaWx5ICVpbiUgYygnVW5hc3NpZ25lZCcpXSA9ICdNaXgnCgoKbm9kZXMuZmFtaWx5Lm1heC5hZGQgPSBjKG5vZGVzLmZhbWlseS5tYXgsIG5hbWVzLm5ldy5mYW1pbHkpCm5vZGVzLmZhbWlseS5tYXguYWRkID0gbm9kZXMuZmFtaWx5Lm1heC5hZGRbdW5pcXVlKGMoYi5ncmFwaFssMV0sIGIuZ3JhcGhbLDJdKSldCgpncmFwaC5jb2xzID0gZGlzY3JldGVfcmFpbmJvdyhsZW5ndGgodW5pcXVlKG5vZGVzLmZhbWlseS5tYXguYWRkKSkpCmdyYXBoLmNvbHMgPSBzYW1wbGUoZ3JhcGguY29scykKbmFtZXMoZ3JhcGguY29scykgPSB1bmlxdWUobm9kZXMuZmFtaWx5Lm1heC5hZGQpCgpnLnBhcnQgPC0gbmV0d29yayhiLmdyYXBoLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEZBTFNFLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgbm9kZS5zaXplID0gMC41LCAKICAgICAgICAgICAgY29sb3IgPSBub2Rlcy5mYW1pbHkubWF4LmFkZCwKICAgICAgICAgICAgcGFsZXR0ZSA9IGdyYXBoLmNvbHMsIG1vZGUgPSAia2FtYWRha2F3YWkiKQpwCmBgYAoKIyBUU05FCmBgYHtyfQoKCmxpYnJhcnkoUnRzbmUpCgoKCgpkIDwtIGlncmFwaDo6ZGlzdGFuY2VzKHRlLmdyYXBoKQpkLm1heCA9IG1heChkWyFpcy5pbmZpbml0ZShkKV0pCgpkW2lzLmluZmluaXRlKGQpXSA9IGQubWF4ICogMS4zCgp0U05FIDwtIFJ0c25lKGQsIGlzX2Rpc3RhbmNlID0gVFJVRSwgZGltcyA9IDIpCgpwbG90KHRTTkUkWVssMV0sIHRTTkUkWVssMl0pCgpgYGAKCgoK